diff --git a/apps/uefi/Mpam.inf b/apps/uefi/Mpam.inf index 0fb23c58..164e8129 100644 --- a/apps/uefi/Mpam.inf +++ b/apps/uefi/Mpam.inf @@ -70,6 +70,8 @@ ../../test_pool/mpam/partition004.c ../../test_pool/mpam/partition005.c ../../test_pool/mpam/partition006.c + ../../test_pool/mpam/partition007.c + ../../test_pool/mpam/partition008.c ../../test_pool/mpam/monitor001.c ../../test_pool/mpam/monitor002.c ../../test_pool/mpam/monitor003.c diff --git a/docs/mpam/arm_sys-mpam_testcase_checklist.csv b/docs/mpam/arm_sys-mpam_testcase_checklist.csv index 4a43089f..ceddf982 100644 --- a/docs/mpam/arm_sys-mpam_testcase_checklist.csv +++ b/docs/mpam/arm_sys-mpam_testcase_checklist.csv @@ -1,23 +1,23 @@ Specification Rule ID,BM,UEFI,Status,Tests,Notes BDJXQ,Yes,Yes,COVERED,monitor001.c,Indirect: monitor001 programs request PARTID and PMG and verifies monitor accounting distinguishes the labels. -DDGSM,Yes,Yes,COVERED,-,No ACS check required: MSC may implement zero or more resource controls. ACS checks controls only when discovered. -RBCMK,Yes,Yes,COVERED,-,No ACS check required: MSC may implement zero or more monitors. ACS checks monitors only when discovered. +DDGSM,,,NOT_COVERED,-,No ACS check required: MSC may implement zero or more resource controls. ACS checks controls only when discovered. +RBCMK,,,NOT_COVERED,-,No ACS check required: MSC may implement zero or more monitors. ACS checks monitors only when discovered. CFXGZ,Yes,Yes,COVERED,reg001.c,Direct: reg001 checks MPAM version EXT bit behavior. FXVBB,Yes,Yes,COVERED,reg004.c,Direct: reg004 checks MPAM-for-RME feature requirements. -LRZGD,Yes,Yes,COVERED,-,No ACS check required: different MPAM architecture versions may coexist. ACS validates each discovered MSC against its advertised version. -PBSTK,Yes,Yes,COVERED,-,No ACS check required: base memory behavior rules always apply. No separate SYS-MPAM ACS check is required. +LRZGD,,,NOT_COVERED,-,No ACS check required: different MPAM architecture versions may coexist. ACS validates each discovered MSC against its advertised version. +PBSTK,,,NOT_COVERED,-,No ACS check required: base memory behavior rules always apply. No separate SYS-MPAM ACS check is required. KXMHB,Yes,Yes,COVERED,reg004.c,"Indirect: non-secure PARTID space is exercised by ACS. Secure, Realm, and Root spaces cannot be directly validated from Non-secure execution." CZHSY,,,NOT_COVERED,-,"Not testable: ACS cannot compare the same PARTID across Secure, Non-secure, Root, and Realm PARTID spaces." RBWRS,,,NOT_COVERED,-,Not testable: ACS cannot access all PARTID spaces to prove each PARTID scope is space-local. -QKQCF,Yes,Yes,COVERED,-,"No ACS check required: Default PARTID 0 is architecturally defined; labeling non-MPAM requesters with it is permitted, not mandatory." +QKQCF,,,NOT_COVERED,-,"No ACS check required: Default PARTID 0 is architecturally defined; labeling non-MPAM requesters with it is permitted, not mandatory." CZSZX,,,NOT_COVERED,-,Not testable: ACS cannot observe non-MPAM requester labeling to prove Default PMG 0 is used. NVNHD,,,NOT_COVERED,-,"Not testable: requester-generation mechanism is system-wide. ACS cannot prove every PE, SMMU, GIC, and non-PE requester path." SLJDR,,,NOT_COVERED,-,"Not testable: non-PE requester-generated PARTID space, PARTID, and PMG values are not directly observable by ACS." VNVMD,,,NOT_COVERED,-,Not testable: non-MPAM requester default/system-specific labeling is not directly controllable or observable by ACS. -SJFGX,Yes,Yes,COVERED,-,No ACS check required: terminating completer has no downstream MPAM propagation to observe. No ACS runtime check is needed. +SJFGX,,,NOT_COVERED,-,No ACS check required: terminating completer has no downstream MPAM propagation to observe. No ACS runtime check is needed. YPHLH,,,NOT_COVERED,-,Not testable: intermediate forwarding of MPAM information is fabric behavior. ACS cannot observe the downstream bundle directly. RZCWR,,,NOT_COVERED,-,Not testable: buffered-request MPAM retention is internal. ACS cannot force or inspect a buffered request path. -MRWWK,Yes,Yes,COVERED,-,No ACS check required: conditional cache-internal storage rule. ACS checks observable controls and monitors when present. +MRWWK,,,NOT_COVERED,-,No ACS check required: conditional cache-internal storage rule. ACS checks observable controls and monitors when present. XPPBS,Yes,Yes,COVERED,partition001.c,Indirect: cache partition tests exercise MSC response to request MPAM information. CWTSC,Yes,Yes,COVERED,partition001.c,Indirect: partition tests exercise per-PARTID control lookup from request MPAM information. JWKJD,,,NOT_COVERED,-,Not testable: cache-line MPAM metadata is internal. ACS cannot inspect the PARTID associated with each line. @@ -32,7 +32,7 @@ CNQCS,,,NOT_COVERED,-,Not testable: ACS cannot observe 4-to-2 bridge reduction f BHHCR,,,NOT_COVERED,-,Not testable: ACS cannot observe whether a 4-to-2 bridge uses static or dynamic reduction. BSLZQ,,,NOT_COVERED,-,Not testable: ACS cannot observe 2-to-4 bridge expansion from MPAM_NS to MPAM_SP. WBZST,,,NOT_COVERED,-,Not testable: ACS cannot prove bridge preservation of physical address space and PARTID space. -YWPPT,Yes,Yes,COVERED,-,No ACS check required: design statement for true two-space components. No separate ACS runtime check is needed. +YWPPT,,,NOT_COVERED,-,No ACS check required: design statement for true two-space components. No separate ACS runtime check is needed. BNKHP,,,NOT_COVERED,-,Not testable: mAP4SPTO2SP is Root PA-space only; Non-secure ACS cannot verify Root-only access. KFDFD,,,NOT_COVERED,-,Not testable: external mapping register is Root PA-space only; Non-secure ACS cannot verify Root-only access. XRQHH,,,NOT_COVERED,-,Not testable: ACS cannot observe fixed MPAM_SP to MPAM_NS bridge signal mapping. @@ -45,8 +45,8 @@ KPNNZ,,,NOT_COVERED,-,Not testable: cache eviction propagation behavior. ACS can JXRZM,Yes,Yes,COVERED,monitor001.c,Direct: CSUMON tests check cache-storage usage monitoring when implemented. RHYKB,Yes,Yes,COVERED,monitor001.c,Direct: CSUMON PMG/PARTID filter tests check cache-storage accounting for matching criteria. LQQJN,,,NOT_COVERED,-,Not testable: write-hit cache-line association is implementation-specific and cache-internal; ACS cannot observe the chosen association. -KBJPW,Yes,Yes,COVERED,-,"No ACS check required: write-hit portion update is permitted but not required, so there is no mandatory ACS behavior to check." -YBYPZ,Yes,Yes,COVERED,-,No ACS check required: reset/default-state behavior. SYS-MPAM ACS does not need a runtime check unless MPAM controls are discovered after boot. +KBJPW,,,NOT_COVERED,-,"No ACS check required: write-hit portion update is permitted but not required, so there is no mandatory ACS behavior to check." +YBYPZ,,,NOT_COVERED,-,No ACS check required: reset/default-state behavior. SYS-MPAM ACS does not need a runtime check unless MPAM controls are discovered after boot. CNGYT,Yes,Yes,COVERED,partition001.c,Direct: cache partitioning tests use CSU measurement to validate capacity-based partitioning behavior. HHQWY,Yes,Yes,COVERED,error008.c,Direct: RIS tests exercise resource/control discovery and RIS-based control or monitor selection. WSXSL,Yes,Yes,COVERED,"partition001.c, error008.c",Direct: partitioning and RIS tests exercise common resource-control selection using PARTID and RIS. @@ -63,18 +63,18 @@ HSFKH,Yes,Yes,COVERED,error012.c,Direct: error012 checks ID register HAS fields XRHKL,,,NOT_COVERED,-,Not testable: virtual PARTID resolution happens before the MSC. ACS cannot observe the pre-resolution virtual PARTID path. GDMQZ,Yes,Yes,COVERED,reg005.c,Direct: reg005 checks MSC four-space capability when MPAM for RME is supported. MMXWZ,Yes,Yes,COVERED,reg001.c,"Indirect: reg001.c is used as a representative MMR access test using MPAMF_BASE_ns from ACPI/platform data. Secure, Realm, and Root base fields are not validated." -QFCTM,Yes,Yes,COVERED,-,No ACS check required: allows controls and monitors to differ by PA space. No standalone ACS check is required. -CZMFT,Yes,Yes,COVERED,-,No ACS check required: defines when a resource can be partitioned. Actual controls are checked only when implemented. -XDZGT,Yes,Yes,COVERED,-,No ACS check required: describes possible RIS support for multiple resources. No standalone ACS check is required. -HLHYS,Yes,Yes,COVERED,-,No ACS check required: lists optional standard partitioning interfaces. ACS checks only implemented interfaces. +QFCTM,,,NOT_COVERED,-,No ACS check required: allows controls and monitors to differ by PA space. No standalone ACS check is required. +CZMFT,,,NOT_COVERED,-,No ACS check required: defines when a resource can be partitioned. Actual controls are checked only when implemented. +XDZGT,,,NOT_COVERED,-,No ACS check required: describes possible RIS support for multiple resources. No standalone ACS check is required. +HLHYS,,,NOT_COVERED,-,No ACS check required: lists optional standard partitioning interfaces. ACS checks only implemented interfaces. GVCBB,Yes,Yes,COVERED,partition001.c,Indirect: partitioning tests check IDR support bits before exercising each advertised control. KWGHQ,Yes,Yes,COVERED,"partition001.c, mem001.c","Indirect: CPOR and MBWPBM tests use portion/PBM width and allocation behavior, but do not directly assert every generic portion property." TZWJG,Yes,Yes,COVERED,feat001.c,Indirect: feat001 exercises PARTID enable/disable behavior; the global-to-MSC scope is not directly asserted. VFJMZ,Yes,Yes,COVERED,feat001.c,Direct: feat001 verifies that a disabled PARTID allocates no resource or receives the lowest priority. WFQFS,,,NOT_COVERED,-,Not testable: ACS cannot directly observe preserved hardware settings across PARTID disable/re-enable; behavior can only give indirect confidence. GMCWW,,,NOT_COVERED,-,"Gap: needs a direct test to disable and re-enable one PARTID, then verify the effect across all RIS values." -CSLHH,,,NOT_COVERED,-,Gap: needs a direct PARTID narrowing test that disables a request PARTID and verifies the enabled status controls request behavior. -QFRFQ,Yes,Yes,COVERED,-,No ACS check required: defines normal enabled-control behavior. Feature-specific tests cover the implemented controls. +CSLHH,Yes,Yes,COVERED,partition008.c,Direct: partition008 disables and re-enables a narrowed request PARTID and verifies that its enabled status controls request behavior. +QFRFQ,,,NOT_COVERED,-,No ACS check required: defines normal enabled-control behavior. Feature-specific tests cover the implemented controls. RNSLZ,Yes,Yes,COVERED,reg006.c,Direct: reg006 checks NFU is not implemented when PARTID narrowing is supported. NJXHP,,,NOT_COVERED,-,Not testable: boot/reset enable state across all PARTIDs and spaces is not directly testable after firmware handoff. XPPGG,Yes,Yes,COVERED,partition001.c,Direct: partition001 checks cache portion partitioning. @@ -93,7 +93,7 @@ DPZKC,Yes,Yes,COVERED,partition006.c,"Indirect: cache partition tests exercise a DKYDB,Yes,Yes,COVERED,mem001.c,Direct: mem001 configures MBWPBM portions and validates bandwidth partition behavior with MBWU counters. GYMDB,Yes,Yes,COVERED,mem001.c,Direct: mem001 programs BWPBM bits through MBWPBM configuration and validates permitted bandwidth behavior. CFHQL,Yes,Yes,COVERED,mem001.c,Indirect: mem001 validates BWPBM behavior as a PBM-style bandwidth portion control. -HVRZS,Yes,Yes,COVERED,-,No ACS check required: lists optional bandwidth partitioning methods. ACS checks only methods that are implemented. +HVRZS,,,NOT_COVERED,-,No ACS check required: lists optional bandwidth partitioning methods. ACS checks only methods that are implemented. WKZBJ,Yes,Yes,COVERED,"mem002.c, mem003.c","Indirect: mem002 and mem003 validate MBWMIN/MBWMAX fraction behavior, but not every bandwidth control type in the table." BPRDL,,,NOT_COVERED,-,Not testable: end-to-end bandwidth guarantee across concatenated MSCs is system-performance behavior that ACS cannot prove reliably. WRXBV,Yes,Yes,COVERED,mem002.c,Direct: mem002 programs MBWMIN under contention and validates bandwidth behavior with MBWU counters. @@ -104,22 +104,22 @@ PWSWQ,,,NOT_COVERED,-,Not testable: accounting-window carry-over is internal and XSYFH,,,NOT_COVERED,-,Not testable: fixed accounting-window state is internal; ACS cannot reliably prove history clear and new-window behavior. ZQXQL,,,NOT_COVERED,-,"Gap: priority partitioning for same-preference conflicts is testable, but no current mapped test covers it." CMZNL,,,NOT_COVERED,-,Not testable: moving accounting-window history aging is internal; ACS cannot reliably prove request aging and removal behavior. -PXSBS,Yes,Yes,COVERED,-,No ACS check required: allows implementation-defined bandwidth tracking methods. No standalone ACS check is required. -ZDTLN,Yes,Yes,COVERED,-,No ACS check required: defines proportional stride as a generic proportional-control instance. No standalone ACS check is required. +PXSBS,,,NOT_COVERED,-,No ACS check required: allows implementation-defined bandwidth tracking methods. No standalone ACS check is required. +ZDTLN,,,NOT_COVERED,-,No ACS check required: defines proportional stride as a generic proportional-control instance. No standalone ACS check is required. ZFCRG,,,NOT_COVERED,-,"Gap: hAS_PROP advertisement is testable through MPAMF_MBW_IDR, but no current mapped test covers it." TKBDC,,,NOT_COVERED,-,Gap: needs a combined proportional-stride + MBWMAX test; current tests cover MBWMAX only. MSPZM,,,NOT_COVERED,-,"Gap: different priorities per MSC are testable through priority controls, but no current mapped test covers them." JJKQP,,,NOT_COVERED,-,Not testable: upstream QoS priority arrival is fabric behavior. ACS cannot observe incoming interconnect priority signals. NGDBL,,,NOT_COVERED,-,Not testable: priority override affects internal MSC operation and downstream transport priority; ACS cannot directly observe it. -MQRPH,Yes,Yes,COVERED,-,No ACS check required: internal priority partitioning is optional even when downstream priority exists. No mandatory ACS check. -RZDSR,Yes,Yes,COVERED,-,No ACS check required: downstream priority partitioning is optional even when internal priority exists. No mandatory ACS check. +MQRPH,,,NOT_COVERED,-,No ACS check required: internal priority partitioning is optional even when downstream priority exists. No mandatory ACS check. +RZDSR,,,NOT_COVERED,-,No ACS check required: downstream priority partitioning is optional even when internal priority exists. No mandatory ACS check. MBNHC,,,NOT_COVERED,-,Not testable: response priority is downstream-to-upstream fabric behavior. ACS cannot directly observe response priority preservation. -JPLMZ,Yes,Yes,COVERED,-,No ACS check required: implementation-defined controls cannot be interpreted generically by ACS. No standalone ACS check is required. -DQTTD,Yes,Yes,COVERED,-,No ACS check required: PARTID narrowing support is optional. ACS checks narrowing behavior only if implemented. +JPLMZ,,,NOT_COVERED,-,No ACS check required: implementation-defined controls cannot be interpreted generically by ACS. No standalone ACS check is required. +DQTTD,,,NOT_COVERED,-,No ACS check required: PARTID narrowing support is optional. ACS checks narrowing behavior only if implemented. RNWBS,,,NOT_COVERED,-,Gap: PARTID narrowing coverage still needs dedicated work; no current test proves this behavior. -GFYDF,,,NOT_COVERED,-,"Gap: iNTPARTID mapping programming is testable when narrowing exists, but no current mapped test covers it." -WCQKV,Yes,Yes,COVERED,-,No ACS check required: defines monitor types. ACS checks monitors only when the corresponding monitor type is implemented. -ZGGMX,Yes,Yes,COVERED,monitor001.c,Indirect: PARTID-based monitor tests use request PARTID filters; narrowing-specific behavior is not directly proven. +GFYDF,Yes,Yes,COVERED,partition007.c,Direct: partition007 programs request-to-internal PARTID mappings and verifies that mapped requests use the selected internal PARTID controls. +WCQKV,,,NOT_COVERED,-,No ACS check required: defines monitor types. ACS checks monitors only when the corresponding monitor type is implemented. +ZGGMX,Yes,Yes,COVERED,partition007.c,Direct: partition007 verifies that monitoring continues to filter on the original request PARTID when PARTID narrowing is enabled. TMCXJ,,,NOT_COVERED,-,"Gap: top-level monitor RIS 0 access is testable, but no current mapped monitor test covers it." YFMQP,Yes,Yes,COVERED,mem001.c,Direct: MBWU tests count payload bytes during memory workloads using MBWU monitor filters. DLSGF,,,NOT_COVERED,-,"Gap: mBWU capture-event behavior is testable if capture registers exist, but no current mapped test covers it." @@ -131,19 +131,19 @@ VJDKF,Yes,Yes,COVERED,"monitor001.c, mem001.c",Indirect: CSU and MBWU monitor te DSCQB,Yes,Yes,COVERED,"monitor005.c, monitor008.c",Indirect: MBWU tests verify VALUE advances/resumes and CSU tests read/write VALUE; no single test covers all VALUE-field behaviors. FWCFZ,Yes,Yes,COVERED,monitor008.c,Direct: monitor008 writes MBWU VALUE to zero and verifies the counter resumes advancing after new traffic. PVNHR,Yes,Yes,COVERED,monitor007.c,Direct: overflow tests write MBWU VALUE near maximum and verify hardware traffic advances the counter. -KCWDZ,Yes,Yes,COVERED,-,No ACS check required: monitor registers are permitted to be RO. No check is needed unless RW behavior is advertised. -XLWYW,Yes,Yes,COVERED,-,No ACS check required: rW monitor reprogramming without disable is permitted. No mandatory ACS behavior to check. +KCWDZ,,,NOT_COVERED,-,No ACS check required: monitor registers are permitted to be RO. No check is needed unless RW behavior is advertised. +XLWYW,,,NOT_COVERED,-,No ACS check required: rW monitor reprogramming without disable is permitted. No mandatory ACS behavior to check. GKWNB,Yes,Yes,COVERED,monitor001.c,Indirect: CSU monitor tests reprogram PMG/PARTID criteria on the same monitor and continue reading VALUE after new traffic. LRTGP,,,NOT_COVERED,-,Not testable: nRDY=1 after settings update can be too transient to observe reliably in ACS execution. NXKSF,Yes,Yes,COVERED,monitor007.c,Direct: MBWU monitor tests fail if the monitor remains not-ready after update/wait paths. -FVFYC,Yes,Yes,COVERED,-,No ACS check required: automatic NRDY clear is not required for event monitors. No mandatory ACS check. -ZTXDS,Yes,Yes,COVERED,-,No ACS check required: software-defined NRDY use is permitted when automatic update is absent. No mandatory ACS check. +FVFYC,,,NOT_COVERED,-,No ACS check required: automatic NRDY clear is not required for event monitors. No mandatory ACS check. +ZTXDS,,,NOT_COVERED,-,No ACS check required: software-defined NRDY use is permitted when automatic update is absent. No mandatory ACS check. VBZTW,Yes,Yes,COVERED,monitor007.c,Direct: MBWU monitor tests wait MAX_NRDY_USEC and fail if NRDY remains set when the value is read. FZHDC,,,NOT_COVERED,-,Gap: needs a capture-event test that reads the capture register NRDY copy and verifies monitor NRDY resets to 0 after capture. YFVPY,,,NOT_COVERED,-,Gap: needs capture-event tests across monitor instances and capture registers; current tests only check prohibited capture capability. NKTBN,,,NOT_COVERED,-,Gap: capture feature coverage is pending; no current ACS test verifies immediate capture completion. NWKJR,,,NOT_COVERED,-,Gap: capture feature coverage is pending; no current ACS test verifies external capture distribution. -SQSMV,Yes,Yes,COVERED,-,No ACS check required: capture reset after copy is permitted but not required. No mandatory ACS check. +SQSMV,,,NOT_COVERED,-,No ACS check required: capture reset after copy is permitted but not required. No mandatory ACS check. WQJKV,Yes,Yes,COVERED,monitor007.c,Direct: MBWU overflow tests drive VALUE past max and verify OFLOW_STATUS or OFLOW_STATUS_L is set. XZXVK,Yes,Yes,COVERED,monitor007.c,"Indirect: monitor007 verifies post-overflow counter freeze/advance behavior, but does not directly assert the wrapped VALUE." NDMMK,Yes,Yes,COVERED,monitor008.c,Direct: monitor008 clears OFLOW_STATUS through MSMON_CFG_MBWU_CTL and fails if the status remains set. @@ -154,7 +154,7 @@ DFWFP,,,NOT_COVERED,-,Gap: capture feature coverage is pending; no current ACS t GNQFY,Yes,Yes,COVERED,monitor008.c,Direct: monitor008 checks overflow status reset when monitor VALUE is written. MGGJW,,,NOT_COVERED,-,Gap: capture feature coverage is pending; no current ACS test verifies external capture distribution to all PARTID spaces. HXNGD,,,NOT_COVERED,-,Gap: capture feature coverage is pending; no current ACS test verifies local capture distribution across address spaces. -RPGBQ,Yes,Yes,COVERED,-,No ACS check required: overflow linkage event is permitted for monitor types without capture-on-overflow. No mandatory ACS check. +RPGBQ,,,NOT_COVERED,-,No ACS check required: overflow linkage event is permitted for monitor types without capture-on-overflow. No mandatory ACS check. MTGGZ,,,NOT_COVERED,-,Gap: capture feature coverage is pending; current OFLOW_FRZ test does not verify freeze caused by a capture event. DYNFP,Yes,Yes,COVERED,error014.c,"Indirect: error014 triggers a second MPAM error after the first and verifies ESR updates, showing continued MSC error handling." WHSLQ,Yes,Yes,COVERED,"error001.c, error003.c, error005.c, error008.c, error010.c, error014.c","Indirect: error tests validate multiple MPAMF_ESR ERRCODE table entries and selected captured fields, but not every table row or security-state case." @@ -174,40 +174,40 @@ HJRZC,Yes,Yes,COVERED,error010.c,Direct: error010 checks undefined RIS handling SQTSJ,Yes,Yes,COVERED,error010.c,Direct: error010 validates ERRCODE 10 for undefined MSMON_CFG_MON_SEL.RIS; read value is UNKNOWN and not directly assertable. VZWYW,,,NOT_COVERED,-,"Gap: needs a test that programs undefined MSMON_CFG_MON_SEL.RIS, then writes another MSMON register and checks ERRCODE 10 or allowed write behavior." PNPQQ,Yes,Yes,COVERED,"error001.c, error003.c, error005.c",Indirect: implemented error tests exercise required reporting for selected supported error conditions. -SJFMB,Yes,Yes,COVERED,-,No ACS check required: hAS_ESR may be zero when no reportable errors exist. No standalone ACS check is required. -VYLZC,Yes,Yes,COVERED,-,No ACS check required: defines selector types used by later error rules. No standalone ACS check is required. -QBQJG,Yes,Yes,COVERED,-,No ACS check required: selector truncation behavior is permitted. ACS only checks invalid selector reporting when required. +SJFMB,,,NOT_COVERED,-,No ACS check required: hAS_ESR may be zero when no reportable errors exist. No standalone ACS check is required. +VYLZC,,,NOT_COVERED,-,No ACS check required: defines selector types used by later error rules. No standalone ACS check is required. +QBQJG,,,NOT_COVERED,-,No ACS check required: selector truncation behavior is permitted. ACS only checks invalid selector reporting when required. PFNBV,Yes,Yes,COVERED,error001.c,Indirect: out-of-range selector tests indirectly exercise invalid selector detection. CZZZK,Yes,Yes,COVERED,error007.c,Direct: error007 checks Unexpected INTERNAL error reporting when PARTID narrowing is supported. -HVNKJ,Yes,Yes,COVERED,-,No ACS check required: lists possible MPAM interrupt sources. ACS checks only implemented interrupt behavior. -TMMMD,Yes,Yes,COVERED,-,No ACS check required: four-space MSC choice of Secure or Non-secure error interrupt is permitted. No mandatory ACS check. -QDSPY,Yes,Yes,COVERED,-,No ACS check required: applies when no reportable error can occur or detection is optional. No standalone ACS check is required. -PPJSM,Yes,Yes,COVERED,-,No ACS check required: error interrupt type may be level-sensitive or edge-triggered. ACS checks the implemented type. +HVNKJ,,,NOT_COVERED,-,No ACS check required: lists possible MPAM interrupt sources. ACS checks only implemented interrupt behavior. +TMMMD,,,NOT_COVERED,-,No ACS check required: four-space MSC choice of Secure or Non-secure error interrupt is permitted. No mandatory ACS check. +QDSPY,,,NOT_COVERED,-,No ACS check required: applies when no reportable error can occur or detection is optional. No standalone ACS check is required. +PPJSM,,,NOT_COVERED,-,No ACS check required: error interrupt type may be level-sensitive or edge-triggered. ACS checks the implemented type. GLPPK,Yes,Yes,COVERED,intr001.c,Direct: intr001 checks level-sensitive error interrupt behavior. NRGQD,,,NOT_COVERED,-,"Gap: level-sensitive error interrupt must not use MSI. This is testable if interrupt type is discoverable, but no current mapped test covers it." KVGWS,Yes,Yes,COVERED,intr002.c,Direct: intr002 checks edge-triggered error interrupt behavior. KVHTZ,,,NOT_COVERED,-,"Gap: mSI error interrupt requiring edge-triggered behavior is testable, but no current mapped test covers it." -ZRDMM,Yes,Yes,COVERED,-,No ACS check required: overflow interrupt support is optional. ACS checks overflow interrupts only if implemented. +ZRDMM,,,NOT_COVERED,-,No ACS check required: overflow interrupt support is optional. ACS checks overflow interrupts only if implemented. YLCVK,Yes,Yes,COVERED,intr003.c,Direct: intr003 checks overflow interrupt generation conditions. YJCDR,Yes,Yes,COVERED,intr005.c,Direct: intr005 checks MSI overflow interrupt edge-triggered requirement. YVSNK,,,NOT_COVERED,-,"Gap: mSMON_OFLOW_SR summary behavior is testable if implemented, but no current mapped test covers it." JLYJJ,Yes,Yes,COVERED,reg001.c,Indirect: reg001.c is used as a representative simple MMR access test. It discovers MSCs and reads memory-mapped MPAM registers. MBDJF,,,NOT_COVERED,-,Not testable: 32-bit MMR single-copy atomicity is a bus atomicity property that ACS cannot prove reliably. -LVCGQ,Yes,Yes,COVERED,-,No ACS check required: greater-than-32-bit MMR atomicity is not required. No ACS check is needed. +LVCGQ,,,NOT_COVERED,-,No ACS check required: greater-than-32-bit MMR atomicity is not required. No ACS check is needed. DMKKT,,,NOT_COVERED,-,"Not testable: locks are per PARTID space. Non-secure ACS cannot verify locks in Secure, Realm, or Root spaces." -VTFQK,Yes,Yes,COVERED,-,No ACS check required: monitor locks may be separate or shared with partition locks. Both designs are permitted. +VTFQK,,,NOT_COVERED,-,No ACS check required: monitor locks may be separate or shared with partition locks. Both designs are permitted. CTGKD,,,NOT_COVERED,-,"Not testable: monitor locks are per PARTID space. Non-secure ACS cannot verify locks in Secure, Realm, or Root spaces." -RDMVJ,Yes,Yes,COVERED,-,No ACS check required: implementation-defined MMRs are permitted above offset 0x3000. No mandatory ACS check. +RDMVJ,,,NOT_COVERED,-,No ACS check required: implementation-defined MMRs are permitted above offset 0x3000. No mandatory ACS check. KNNBY,Yes,Yes,COVERED,reg001.c,Indirect: reg001.c is used as a representative MPAM feature-page register access test. Reserved/unimplemented register locations are not exhaustively probed. ZPWLC,Yes,Yes,COVERED,reg001.c,Indirect: reg001.c is used as a representative Non-secure feature-page access test. Other PA-space feature pages are not accessible to ACS. DYDFT,Yes,Yes,COVERED,reg001.c,"Indirect: reg001.c is used as a representative Non-secure feature-page base access test. Secure, Realm, and Root bases cannot be validated from Non-secure ACS." -HXLMF,Yes,Yes,COVERED,-,No ACS check required: different feature-page base addresses are permitted and overlap avoidance is a recommendation. No mandatory ACS check. +HXLMF,,,NOT_COVERED,-,No ACS check required: different feature-page base addresses are permitted and overlap avoidance is a recommendation. No mandatory ACS check. QCYPH,,,NOT_COVERED,-,"Gap: feature-page alignment and containment are testable from platform data, but no current mapped test covers them." -FLWNK,Yes,Yes,COVERED,-,No ACS check required: non-MPAM MMR placement in the feature page is permitted only for trapped MMRs. No mandatory ACS check. +FLWNK,,,NOT_COVERED,-,No ACS check required: non-MPAM MMR placement in the feature page is permitted only for trapped MMRs. No mandatory ACS check. GTKCX,Yes,Yes,COVERED,reg001.c,Indirect: all SYS-MPAM MMR-based tests require the Non-secure MPAM feature page to exist. DFBBW,,,NOT_COVERED,-,Not testable: secure feature page existence cannot be verified from Non-secure ACS execution. FYTQL,,,NOT_COVERED,-,Not testable: realm and Root feature pages cannot be verified from Non-secure ACS execution. -KZBWG,Yes,Yes,COVERED,-,No ACS check required: rO MMR sharing across security states is permitted when contents match. ACS cannot and need not check sharing. +KZBWG,,,NOT_COVERED,-,No ACS check required: rO MMR sharing across security states is permitted when contents match. ACS cannot and need not check sharing. YCBKB,Yes,Yes,COVERED,"partition004.c, mem001.c",Indirect: PBM partitioning tests program CPBM/MBWPBM words using IDR widths and PBM offsets. TVYHS,,,NOT_COVERED,-,"Gap: unused PBM bits being RES0 is testable, but no current mapped test covers it." RJFKG,Yes,Yes,COVERED,"partition005.c, mem002.c","Indirect: fractional partitioning helpers program values using percentage minus one lsb, and tests validate resulting behavior." diff --git a/docs/mpam/arm_sys-mpam_testcase_checklist.json b/docs/mpam/arm_sys-mpam_testcase_checklist.json index db67e6d2..721f6d83 100644 --- a/docs/mpam/arm_sys-mpam_testcase_checklist.json +++ b/docs/mpam/arm_sys-mpam_testcase_checklist.json @@ -17,10 +17,10 @@ }, "status_definitions": { "COVERED": { - "meaning": "Rule is closed for ACS coverage: directly covered, indirectly covered, or no standalone ACS check is required. See Notes for the reason." + "meaning": "Rule has a direct or indirect ACS test mapped to it." }, "NOT_COVERED": { - "meaning": "Rule is not closed by ACS coverage. It is either not testable by ACS or is a coverage gap. See Notes for the reason." + "meaning": "Rule has no standalone ACS test. See Notes to determine whether no ACS check is required, the rule is not testable by ACS, or coverage is pending." } }, "rules": [ @@ -39,7 +39,7 @@ }, { "rule_id": "DDGSM", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: MSC may implement zero or more resource controls. ACS checks controls only when discovered.", "sub_rules": [], @@ -50,7 +50,7 @@ }, { "rule_id": "RBCMK", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: MSC may implement zero or more monitors. ACS checks monitors only when discovered.", "sub_rules": [], @@ -87,7 +87,7 @@ }, { "rule_id": "LRZGD", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: different MPAM architecture versions may coexist. ACS validates each discovered MSC against its advertised version.", "sub_rules": [], @@ -98,7 +98,7 @@ }, { "rule_id": "PBSTK", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: base memory behavior rules always apply. No separate SYS-MPAM ACS check is required.", "sub_rules": [], @@ -144,7 +144,7 @@ }, { "rule_id": "QKQCF", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: Default PARTID 0 is architecturally defined; labeling non-MPAM requesters with it is permitted, not mandatory.", "sub_rules": [], @@ -199,7 +199,7 @@ }, { "rule_id": "SJFGX", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: terminating completer has no downstream MPAM propagation to observe. No ACS runtime check is needed.", "sub_rules": [], @@ -232,7 +232,7 @@ }, { "rule_id": "MRWWK", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: conditional cache-internal storage rule. ACS checks observable controls and monitors when present.", "sub_rules": [], @@ -411,7 +411,7 @@ }, { "rule_id": "YWPPT", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: design statement for true two-space components. No separate ACS runtime check is needed.", "sub_rules": [], @@ -558,7 +558,7 @@ }, { "rule_id": "KBJPW", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: write-hit portion update is permitted but not required, so there is no mandatory ACS behavior to check.", "sub_rules": [], @@ -569,7 +569,7 @@ }, { "rule_id": "YBYPZ", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: reset/default-state behavior. SYS-MPAM ACS does not need a runtime check unless MPAM controls are discovered after boot.", "sub_rules": [], @@ -789,7 +789,7 @@ }, { "rule_id": "QFCTM", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: allows controls and monitors to differ by PA space. No standalone ACS check is required.", "sub_rules": [], @@ -800,7 +800,7 @@ }, { "rule_id": "CZMFT", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: defines when a resource can be partitioned. Actual controls are checked only when implemented.", "sub_rules": [], @@ -811,7 +811,7 @@ }, { "rule_id": "XDZGT", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: describes possible RIS support for multiple resources. No standalone ACS check is required.", "sub_rules": [], @@ -822,7 +822,7 @@ }, { "rule_id": "HLHYS", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: lists optional standard partitioning interfaces. ACS checks only implemented interfaces.", "sub_rules": [], @@ -908,9 +908,11 @@ }, { "rule_id": "CSLHH", - "status": "NOT_COVERED", - "tests": [], - "notes": "Gap: needs a direct PARTID narrowing test that disables a request PARTID and verifies the enabled status controls request behavior.", + "status": "COVERED", + "tests": [ + "partition008.c" + ], + "notes": "Direct: partition008 disables and re-enables a narrowed request PARTID and verifies that its enabled status controls request behavior.", "sub_rules": [], "platforms": [ "BM", @@ -919,7 +921,7 @@ }, { "rule_id": "QFRFQ", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: defines normal enabled-control behavior. Feature-specific tests cover the implemented controls.", "sub_rules": [], @@ -1160,7 +1162,7 @@ }, { "rule_id": "HVRZS", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: lists optional bandwidth partitioning methods. ACS checks only methods that are implemented.", "sub_rules": [], @@ -1290,7 +1292,7 @@ }, { "rule_id": "PXSBS", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: allows implementation-defined bandwidth tracking methods. No standalone ACS check is required.", "sub_rules": [], @@ -1301,7 +1303,7 @@ }, { "rule_id": "ZDTLN", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: defines proportional stride as a generic proportional-control instance. No standalone ACS check is required.", "sub_rules": [], @@ -1367,7 +1369,7 @@ }, { "rule_id": "MQRPH", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: internal priority partitioning is optional even when downstream priority exists. No mandatory ACS check.", "sub_rules": [], @@ -1378,7 +1380,7 @@ }, { "rule_id": "RZDSR", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: downstream priority partitioning is optional even when internal priority exists. No mandatory ACS check.", "sub_rules": [], @@ -1400,7 +1402,7 @@ }, { "rule_id": "JPLMZ", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: implementation-defined controls cannot be interpreted generically by ACS. No standalone ACS check is required.", "sub_rules": [], @@ -1411,7 +1413,7 @@ }, { "rule_id": "DQTTD", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: PARTID narrowing support is optional. ACS checks narrowing behavior only if implemented.", "sub_rules": [], @@ -1433,9 +1435,11 @@ }, { "rule_id": "GFYDF", - "status": "NOT_COVERED", - "tests": [], - "notes": "Gap: iNTPARTID mapping programming is testable when narrowing exists, but no current mapped test covers it.", + "status": "COVERED", + "tests": [ + "partition007.c" + ], + "notes": "Direct: partition007 programs request-to-internal PARTID mappings and verifies that mapped requests use the selected internal PARTID controls.", "sub_rules": [], "platforms": [ "BM", @@ -1444,7 +1448,7 @@ }, { "rule_id": "WCQKV", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: defines monitor types. ACS checks monitors only when the corresponding monitor type is implemented.", "sub_rules": [], @@ -1457,9 +1461,9 @@ "rule_id": "ZGGMX", "status": "COVERED", "tests": [ - "monitor001.c" + "partition007.c" ], - "notes": "Indirect: PARTID-based monitor tests use request PARTID filters; narrowing-specific behavior is not directly proven.", + "notes": "Direct: partition007 verifies that monitoring continues to filter on the original request PARTID when PARTID narrowing is enabled.", "sub_rules": [], "platforms": [ "BM", @@ -1605,7 +1609,7 @@ }, { "rule_id": "KCWDZ", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: monitor registers are permitted to be RO. No check is needed unless RW behavior is advertised.", "sub_rules": [], @@ -1616,7 +1620,7 @@ }, { "rule_id": "XLWYW", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: rW monitor reprogramming without disable is permitted. No mandatory ACS behavior to check.", "sub_rules": [], @@ -1664,7 +1668,7 @@ }, { "rule_id": "FVFYC", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: automatic NRDY clear is not required for event monitors. No mandatory ACS check.", "sub_rules": [], @@ -1675,7 +1679,7 @@ }, { "rule_id": "ZTXDS", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: software-defined NRDY use is permitted when automatic update is absent. No mandatory ACS check.", "sub_rules": [], @@ -1743,7 +1747,7 @@ }, { "rule_id": "SQSMV", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: capture reset after copy is permitted but not required. No mandatory ACS check.", "sub_rules": [], @@ -1877,7 +1881,7 @@ }, { "rule_id": "RPGBQ", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: overflow linkage event is permitted for monitor types without capture-on-overflow. No mandatory ACS check.", "sub_rules": [], @@ -2131,7 +2135,7 @@ }, { "rule_id": "SJFMB", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: hAS_ESR may be zero when no reportable errors exist. No standalone ACS check is required.", "sub_rules": [], @@ -2142,7 +2146,7 @@ }, { "rule_id": "VYLZC", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: defines selector types used by later error rules. No standalone ACS check is required.", "sub_rules": [], @@ -2153,7 +2157,7 @@ }, { "rule_id": "QBQJG", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: selector truncation behavior is permitted. ACS only checks invalid selector reporting when required.", "sub_rules": [], @@ -2190,7 +2194,7 @@ }, { "rule_id": "HVNKJ", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: lists possible MPAM interrupt sources. ACS checks only implemented interrupt behavior.", "sub_rules": [], @@ -2201,7 +2205,7 @@ }, { "rule_id": "TMMMD", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: four-space MSC choice of Secure or Non-secure error interrupt is permitted. No mandatory ACS check.", "sub_rules": [], @@ -2212,7 +2216,7 @@ }, { "rule_id": "QDSPY", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: applies when no reportable error can occur or detection is optional. No standalone ACS check is required.", "sub_rules": [], @@ -2223,7 +2227,7 @@ }, { "rule_id": "PPJSM", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: error interrupt type may be level-sensitive or edge-triggered. ACS checks the implemented type.", "sub_rules": [], @@ -2282,7 +2286,7 @@ }, { "rule_id": "ZRDMM", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: overflow interrupt support is optional. ACS checks overflow interrupts only if implemented.", "sub_rules": [], @@ -2354,7 +2358,7 @@ }, { "rule_id": "LVCGQ", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: greater-than-32-bit MMR atomicity is not required. No ACS check is needed.", "sub_rules": [], @@ -2376,7 +2380,7 @@ }, { "rule_id": "VTFQK", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: monitor locks may be separate or shared with partition locks. Both designs are permitted.", "sub_rules": [], @@ -2398,7 +2402,7 @@ }, { "rule_id": "RDMVJ", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: implementation-defined MMRs are permitted above offset 0x3000. No mandatory ACS check.", "sub_rules": [], @@ -2448,7 +2452,7 @@ }, { "rule_id": "HXLMF", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: different feature-page base addresses are permitted and overlap avoidance is a recommendation. No mandatory ACS check.", "sub_rules": [], @@ -2470,7 +2474,7 @@ }, { "rule_id": "FLWNK", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: non-MPAM MMR placement in the feature page is permitted only for trapped MMRs. No mandatory ACS check.", "sub_rules": [], @@ -2516,7 +2520,7 @@ }, { "rule_id": "KZBWG", - "status": "COVERED", + "status": "NOT_COVERED", "tests": [], "notes": "No ACS check required: rO MMR sharing across security states is permitted when contents match. ACS cannot and need not check sharing.", "sub_rules": [], diff --git a/docs/mpam/arm_sys-mpam_testcase_checklist.md b/docs/mpam/arm_sys-mpam_testcase_checklist.md index cefec336..eaaa1b80 100644 --- a/docs/mpam/arm_sys-mpam_testcase_checklist.md +++ b/docs/mpam/arm_sys-mpam_testcase_checklist.md @@ -8,8 +8,8 @@ can be downloaded, filtered, and reviewed without duplicating the full table her | Status | Count | | --- | ---: | -| `COVERED` | 138 | -| `NOT_COVERED` | 75 | +| `COVERED` | 97 | +| `NOT_COVERED` | 116 | | `TOTAL` | 213 | ## Platform Information @@ -40,8 +40,8 @@ The CSV contains these columns: | Status | Meaning | | --- | --- | -| `COVERED` | Rule is closed for ACS coverage: directly covered, indirectly covered, or no standalone ACS check is required. See Notes for the reason. | -| `NOT_COVERED` | Rule is not closed by ACS coverage. It is either not testable by ACS or is a coverage gap. See Notes for the reason. | +| `COVERED` | Rule has a direct or indirect ACS test mapped to it. | +| `NOT_COVERED` | Rule has no standalone ACS test. See Notes to determine whether no ACS check is required, the rule is not testable by ACS, or coverage is pending. | ## Maintenance diff --git a/test_pool/mpam/error003.c b/test_pool/mpam/error003.c index 4d25884e..5f319979 100644 --- a/test_pool/mpam/error003.c +++ b/test_pool/mpam/error003.c @@ -21,98 +21,190 @@ #include "acs_mpam_reg.h" #include "val_interface.h" +/* + * Algorithm: + * 1. Iterate over MSCs that implement error reporting. + * 2. Find an MPAMCFG_* register that can be accessed with a valid selector. + * 3. Save the selector, error-control register, and selected register value. + * 4. Program PARTID_MAX + 1 into MPAMCFG_PART_SEL.PARTID_SEL. + * 5. Continue only if the out-of-range PARTID selector is retained. + * 6. Clear any selector-write error and write the saved MPAMCFG_* value. + * 7. Accept either no error or an out-of-range PARTID error. + * 8. Restore the selector and error state. + */ + #define TEST_NUM ACS_MPAM_ERROR_TEST_NUM_BASE + 3 -#define TEST_DESC "Check MSMON ConfigID out-of-range Err " +#define TEST_DESC "Check MPAMCFG write with out-of-range PARTID" #define TEST_RULE "ZWRQW" -static -void payload(void) +static uint32_t +find_config_register(uint32_t msc_index, uint32_t valid_partid, + uint32_t selector_original, uint32_t *config_reg, + uint32_t *config_value, uint32_t *selector_valid) { + uint32_t data; + uint32_t has_ris; + uint32_t ris; + uint32_t rsrc_count; + uint32_t rsrc_index; + + /* Narrowing mappings are configured using a request PARTID selector. */ + if (val_mpam_msc_supports_partid_nrw(msc_index)) { + data = BITFIELD_WRITE(selector_original, PART_SEL_PARTID_SEL, valid_partid); + data = BITFIELD_WRITE(data, PART_SEL_INTERNAL, 0); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, data); + + *config_reg = REG_MPAMCFG_INTPARTID; + *config_value = val_mpam_mmr_read(msc_index, *config_reg); + *selector_valid = data; + return ACS_STATUS_PASS; + } + + /* Otherwise, choose one implemented partition-control register. */ + has_ris = val_mpam_msc_supports_ris(msc_index); + rsrc_count = has_ris ? val_mpam_get_info(MPAM_MSC_RSRC_COUNT, msc_index, 0) : 1; + if ((rsrc_count == 0) || (rsrc_count == MPAM_INVALID_INFO)) + return ACS_STATUS_SKIP; + + for (rsrc_index = 0; rsrc_index < rsrc_count; rsrc_index++) { + ris = has_ris ? val_mpam_get_info(MPAM_MSC_RSRC_RIS, msc_index, rsrc_index) : + BITFIELD_READ(PART_SEL_RIS, selector_original); + data = BITFIELD_WRITE(selector_original, PART_SEL_PARTID_SEL, valid_partid); + data = BITFIELD_WRITE(data, PART_SEL_INTERNAL, 0); + data = BITFIELD_WRITE(data, PART_SEL_RIS, ris); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, data); + + if (val_mpam_supports_ccap(msc_index)) + *config_reg = REG_MPAMCFG_CMAX; + else if (val_mpam_supports_cpor(msc_index)) + *config_reg = REG_MPAMCFG_CPBM; + else if (val_mpam_msc_supports_mbw_max(msc_index)) + *config_reg = REG_MPAMCFG_MBW_MAX; + else + continue; + + *config_value = val_mpam_mmr_read(msc_index, *config_reg); + *selector_valid = data; + return ACS_STATUS_PASS; + } + + return ACS_STATUS_SKIP; +} - uint16_t mon_count; - uint32_t index; +static void +payload(void) +{ + uint32_t msc_index; uint32_t status; uint32_t pe_index; + uint32_t total_nodes; uint32_t mpamf_ecr; uint32_t esr_errcode; - uint32_t total_nodes; + uint32_t selector_original; + uint32_t selector_valid; + uint32_t selector_test; + uint32_t selector_readback; + uint32_t config_reg; + uint32_t config_value; + uint32_t max_partid; + uint32_t out_of_range_partid; uint32_t test_fail = 0; uint32_t test_skip = 1; pe_index = val_pe_get_index_mpid(val_pe_get_mpid()); total_nodes = val_mpam_get_msc_count(); - for (index = 0; index < total_nodes; index++) { - - mon_count = 0; - - if (!val_mpam_msc_supports_esr(index)) { - val_print(DEBUG, "\n MSC index %d does not support ESR", index); + /* Validate the out-of-range configuration-write behavior on each MSC. */ + for (msc_index = 0; msc_index < total_nodes; msc_index++) { + if (!val_mpam_msc_supports_esr(msc_index)) { + val_print(DEBUG, "\n MSC index %d does not support ESR", msc_index); continue; } - /* Read MPAMF_ECR before generating error. This will be used to restore to default later */ - mpamf_ecr = val_mpam_mmr_read(index, REG_MPAMF_ECR); - status = val_mpam_msc_reset_errcode(index); - - if (!status) { - val_set_status(pe_index, RESULT_FAIL(01)); - return; + max_partid = val_mpam_get_max_partid(msc_index); + if (max_partid == BITFIELD_READ(PART_SEL_PARTID_SEL, ACS_UINT32_MAX)) { + val_print(DEBUG, "\n MSC index %d has no out-of-range PARTID value", + msc_index); + continue; } - - /* Use the count for the monitor type selected by the error helper. */ - if (val_mpam_msc_supports_mon(index)) { - if (val_mpam_supports_csumon(index)) { - mon_count = val_mpam_get_csumon_count(index); - val_print(DEBUG, - "\n MSC implements %d CSU Monitors", mon_count); - } else if (val_mpam_msc_supports_mbwumon(index)) { - mon_count = val_mpam_get_mbwumon_count(index); - val_print(DEBUG, - "\n MSC implements %d MBWU Monitors", mon_count); - } + out_of_range_partid = max_partid + 1U; + + /* Preserve state before selecting a register used by the test. */ + mpamf_ecr = val_mpam_mmr_read(msc_index, REG_MPAMF_ECR); + selector_original = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + status = find_config_register(msc_index, max_partid, selector_original, + &config_reg, &config_value, &selector_valid); + if (status == ACS_STATUS_SKIP) { + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, selector_original); + val_mpam_mmr_write(msc_index, REG_MPAMF_ECR, mpamf_ecr); + continue; } - if (mon_count == 0) + /* Clear stale errors before programming the out-of-range PARTID. */ + if (!val_mpam_msc_reset_errcode(msc_index)) { + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, selector_original); + val_mpam_mmr_write(msc_index, REG_MPAMF_ECR, mpamf_ecr); + test_skip = 0; + test_fail++; continue; + } - /* Test runs on atleast one MSC */ - test_skip = 0; + selector_test = BITFIELD_WRITE(selector_valid, PART_SEL_PARTID_SEL, + out_of_range_partid); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, selector_test); + selector_readback = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + + /* Skip when the implementation rejects or truncates the selector. */ + if (BITFIELD_READ(PART_SEL_PARTID_SEL, selector_readback) != + out_of_range_partid) { + val_print(DEBUG, "\n Out-of-range PARTID was not retained for MSC %d", + msc_index); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, selector_original); + val_mpam_msc_reset_errcode(msc_index); + val_mpam_mmr_write(msc_index, REG_MPAMF_ECR, mpamf_ecr); + continue; + } - /* Generate MSMON config ID out-of-range error */ - val_mpam_msc_generate_msmon_config_error(index, mon_count); + /* Isolate the error, if any, raised by the MPAMCFG_* write. */ + if (!val_mpam_msc_reset_errcode(msc_index)) { + test_fail++; + goto cleanup; + } - /* Wait for some time for the error to be reflected in MPAMF_ESR */ + test_skip = 0; + val_mpam_mmr_write(msc_index, config_reg, config_value); val_time_delay_ms(100 * ONE_MILLISECOND); - - /* Read Error Status Register and check if the error code is recorded */ - esr_errcode = val_mpam_msc_get_errcode(index); - val_print(DEBUG, "\n Error code read is %llx", esr_errcode); - - if (esr_errcode != ESR_ERRCODE_MSMONCFG_ID_RANGE) - { - val_print(ERROR, - "\n Expected errcode: %d", ESR_ERRCODE_MSMONCFG_ID_RANGE); + esr_errcode = val_mpam_msc_get_errcode(msc_index); + val_print(DEBUG, "\n Error code read is %d", esr_errcode); + + /* Detection is optional, but a detected out-of-range PARTID reports error 1. */ + if ((esr_errcode != ESR_ERRCODE_NO_ERROR) && + (esr_errcode != ESR_ERRCODE_PARTID_SEL_RANGE)) { + val_print(ERROR, "\n Expected errcode: %d or no error", + ESR_ERRCODE_PARTID_SEL_RANGE); val_print(ERROR, "\n Actual errcode: %d", esr_errcode); test_fail++; } - /* Restore Error Control Register original settings */ - val_mpam_mmr_write(index, REG_MPAMF_ECR, mpamf_ecr); +cleanup: + /* The target PARTID is unknown, so only selector and error state can be restored. */ + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, selector_original); + if (!val_mpam_msc_reset_errcode(msc_index)) + test_fail++; + val_mpam_mmr_write(msc_index, REG_MPAMF_ECR, mpamf_ecr); } - if (test_skip) - val_set_status(pe_index, RESULT_SKIP(01)); - else if (test_fail) + if (test_fail) val_set_status(pe_index, RESULT_FAIL(02)); + else if (test_skip) + val_set_status(pe_index, RESULT_SKIP(01)); else val_set_status(pe_index, RESULT_PASS); - return; } -uint32_t error003_entry(uint32_t num_pe) +uint32_t +error003_entry(uint32_t num_pe) { - uint32_t status = ACS_STATUS_FAIL; num_pe = 1; @@ -121,11 +213,9 @@ uint32_t error003_entry(uint32_t num_pe) status = val_initialize_test(TEST_NUM, TEST_DESC, num_pe); if (status != ACS_STATUS_SKIP) - val_run_test_payload(TEST_NUM, num_pe, payload, 0); + val_run_test_payload(TEST_NUM, num_pe, payload, 0); - /* get the result from all PE and check for failure */ status = val_check_for_error(TEST_NUM, num_pe, TEST_RULE); - val_report_status(0, ACS_END(TEST_NUM), NULL); return status; diff --git a/test_pool/mpam/error005.c b/test_pool/mpam/error005.c index 0dce3439..3a7bf3cf 100644 --- a/test_pool/mpam/error005.c +++ b/test_pool/mpam/error005.c @@ -17,96 +17,162 @@ #include "acs_val.h" #include "acs_pe.h" +#include "acs_memory.h" #include "acs_mpam.h" #include "acs_mpam_reg.h" #include "val_interface.h" +/* + * Algorithm: + * 1. Iterate over MSCs that implement error reporting and resource monitors. + * 2. Save the partition and monitor selectors. + * 3. Iterate over each resource instance supported by the MSC. + * 4. For each supported monitor type, obtain the implemented monitor count. + * 5. Select monitor 0, clear the error status, and write the monitor count as + * the first out-of-range monitor selector. + * 6. Read back the selector and error status. + * 7. Accept the selector being retained without an error, rejected with a + * monitor-range error, or truncated at the implemented selector width. + * 8. Restore the selectors and clear the error status. + */ + #define TEST_NUM ACS_MPAM_ERROR_TEST_NUM_BASE + 5 #define TEST_DESC "Check MSC MONITOR selection range Err " #define TEST_RULE "VMTBP" +static uint32_t +check_monitor_selector(uint32_t msc_index, uint32_t mon_count) +{ + uint32_t selector_original; + uint32_t selector_base; + uint32_t selector_test; + uint32_t selector_readback; + uint32_t esr_errcode; + uint32_t status = ACS_STATUS_PASS; + + /* The monitor count must identify an out-of-range value representable by the field. */ + if ((mon_count == 0) || (mon_count > BITFIELD_READ(MON_SEL_MON_SEL, ACS_UINT32_MAX))) + return ACS_STATUS_SKIP; + + /* Save the selector before selecting a known valid monitor instance. */ + selector_original = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + val_print(DEBUG, "\n RIS: %d, monitor count: %d", + BITFIELD_READ(MON_SEL_RIS, selector_original), mon_count); + selector_base = BITFIELD_WRITE(selector_original, MON_SEL_MON_SEL, 0); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, selector_base); + + /* Clear stale errors before programming the out-of-range selector. */ + if (!val_mpam_msc_reset_errcode(msc_index)) { + status = ACS_STATUS_FAIL; + goto cleanup; + } + + /* Program the first selector beyond the implemented monitor range. */ + selector_test = BITFIELD_WRITE(selector_base, MON_SEL_MON_SEL, mon_count); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, selector_test); + val_mem_issue_dsb(); + + /* Read back the selector and any error reported by the MSC. */ + selector_readback = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + val_time_delay_ms(100 * ONE_MILLISECOND); + esr_errcode = val_mpam_msc_get_errcode(msc_index); + + /* Classify the observed selector and error behavior. */ + if ((selector_readback == selector_test) && (esr_errcode == ESR_ERRCODE_NO_ERROR)) { + val_print(DEBUG, "\n Out-of-range monitor selector retained for MSC %d", msc_index); + } else if ((selector_readback == selector_base) && + (esr_errcode == ESR_ERRCODE_MON_RANGE)) { + val_print(DEBUG, "\n Out-of-range monitor selector rejected for MSC %d", msc_index); + } else if (((mon_count & (mon_count - 1U)) == 0) && + (selector_readback == selector_base) && (esr_errcode == ESR_ERRCODE_NO_ERROR)) { + val_print(DEBUG, "\n Monitor selector boundary truncated for MSC %d", msc_index); + status = ACS_STATUS_SKIP; + } else { + val_print(ERROR, "\n Invalid monitor selector handling for MSC %d", msc_index); + val_print(ERROR, "\n Selector readback: 0x%x, errcode: %d", + selector_readback, esr_errcode); + status = ACS_STATUS_FAIL; + } + +cleanup: + /* Restore the original selector and leave the error status clear. */ + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, selector_original); + if (!val_mpam_msc_reset_errcode(msc_index)) + status = ACS_STATUS_FAIL; + + return status; +} + static void payload(void) { - - uint16_t mon_count = 0; uint32_t index; uint32_t status; uint32_t pe_index; uint32_t total_nodes; - uint32_t mpamf_ecr; - uint32_t esr_errcode; + uint32_t rsrc_index; + uint32_t rsrc_count; + uint32_t has_ris; + uint32_t part_sel_original; + uint32_t mon_sel_original; uint32_t test_fail = 0; uint32_t test_skip = 1; pe_index = val_pe_get_index_mpid(val_pe_get_mpid()); total_nodes = val_mpam_get_msc_count(); + /* Validate monitor selector handling on every eligible MSC. */ for (index = 0; index < total_nodes; index++) { + /* Error reporting is required to observe a rejected selector. */ if (!val_mpam_msc_supports_esr(index)) { val_print(DEBUG, "\n MSC index %d does not support ESR", index); continue; } - /* Read the number of CSU monitors and MBWU monitors implemented in this MSC */ - if (val_mpam_msc_supports_mon(index)) { - if (val_mpam_supports_csumon(index)) { - mon_count += val_mpam_get_csumon_count(index); - val_print(DEBUG, - "\n MSC implements %d CSU Monitors", mon_count); - } - - if (val_mpam_msc_supports_mbwumon(index)) { - mon_count += val_mpam_get_mbwumon_count(index); - val_print(DEBUG, - "\n MSC implements %d MBWU Monitors", val_mpam_get_mbwumon_count(index)); - } - } - - /* - * Skip this MSC if it doesn't implement error interrupt - * support (or) if it doesn't implement any monitors - */ - if ((mon_count == 0)) { + /* At least one monitor type is required to obtain a monitor count. */ + if (!val_mpam_msc_supports_mon(index)) { val_print(DEBUG, "\n MSC %d does not implement any MSMON. Skipping MSC..", index); continue; } - /* Read MPAMF_ECR before generating error. This will be used to restore to default later */ - mpamf_ecr = val_mpam_mmr_read(index, REG_MPAMF_ECR); - status = val_mpam_msc_reset_errcode(index); - - if (!status) { - val_set_status(pe_index, RESULT_FAIL(01)); - return; - } - - /* Test runs on atleast one MSC */ - test_skip = 0; - - /* Generate monitor selection range (MSR) error */ - val_mpam_msc_generate_msr_error(index, mon_count); - - /* Wait for some time for the error to be reflected in MPAMF_ESR */ - val_time_delay_ms(100 * ONE_MILLISECOND); - - /* Read Error Status Register and check if the error code is recorded */ - esr_errcode = val_mpam_msc_get_errcode(index); - val_print(DEBUG, "\n Error code read is %llx", esr_errcode); - - if (esr_errcode != ESR_ERRCODE_MON_RANGE) - { - val_print(ERROR, "\n Expected errcode: %d", ESR_ERRCODE_MON_RANGE); - val_print(ERROR, "\n Actual errcode: %d", esr_errcode); - test_fail++; + /* Preserve selector state and determine the resources to examine. */ + part_sel_original = val_mpam_mmr_read(index, REG_MPAMCFG_PART_SEL); + mon_sel_original = val_mpam_mmr_read(index, REG_MSMON_CFG_MON_SEL); + has_ris = val_mpam_msc_supports_ris(index); + rsrc_count = has_ris ? val_mpam_get_info(MPAM_MSC_RSRC_COUNT, index, 0) : 1; + + for (rsrc_index = 0; rsrc_index < rsrc_count; rsrc_index++) { + /* Select the current resource when resource selection is implemented. */ + if (has_ris) + val_mpam_memory_configure_ris_sel(index, rsrc_index); + + /* Check the selector boundary for cache-storage monitors. */ + if (val_mpam_supports_csumon(index)) { + status = check_monitor_selector(index, val_mpam_get_csumon_count(index)); + if (status != ACS_STATUS_SKIP) + test_skip = 0; + if (status == ACS_STATUS_FAIL) + test_fail++; + } + + /* Check the selector boundary for memory-bandwidth monitors. */ + if (val_mpam_msc_supports_mbwumon(index)) { + status = check_monitor_selector(index, val_mpam_get_mbwumon_count(index)); + if (status != ACS_STATUS_SKIP) + test_skip = 0; + if (status == ACS_STATUS_FAIL) + test_fail++; + } } - /* Restore Error Control Register original settings */ - val_mpam_mmr_write(index, REG_MPAMF_ECR, mpamf_ecr); + /* Restore the selectors before moving to the next MSC. */ + val_mpam_mmr_write(index, REG_MSMON_CFG_MON_SEL, mon_sel_original); + val_mpam_mmr_write(index, REG_MPAMCFG_PART_SEL, part_sel_original); } + /* Report the aggregate result from all eligible resources and monitor types. */ if (test_skip) val_set_status(pe_index, RESULT_SKIP(01)); else if (test_fail) diff --git a/test_pool/mpam/error008.c b/test_pool/mpam/error008.c index 8daac1f4..ed32c16c 100644 --- a/test_pool/mpam/error008.c +++ b/test_pool/mpam/error008.c @@ -21,37 +21,23 @@ #include "acs_mpam_reg.h" #include "val_interface.h" +/* + * Algorithm: + * 1. Iterate over MSCs that implement ESR and RIS. + * 2. Save MPAMF_ECR and MPAMCFG_PART_SEL, then clear MPAMF_ESR.ERRCODE. + * 3. Write RIS_MAX + 1 to MPAMCFG_PART_SEL.RIS. + * 4. Read back MPAMCFG_PART_SEL and MPAMF_ESR.ERRCODE. + * 5. Accept either behavior permitted by VKKDP: + * - the undefined RIS is retained without an immediate error, or + * - the selector is not updated and undefined-RIS error 8 is reported. + * 6. Restore the selector and error-control state and clear MPAMF_ESR. + * 7. Pass if every eligible MSC follows one of the permitted behaviors. + */ + #define TEST_NUM ACS_MPAM_ERROR_TEST_NUM_BASE + 8 #define TEST_DESC "Check Undef RIS MPAMCFG_PART_SEL error" #define TEST_RULE "VKKDP" -static uint32_t max_ris_index; - -static -uint32_t -check_for_raz_wi(uint32_t msc_index, uint32_t reg_offset) -{ - /* With out-of-range RIS value programmed to MPAM_CFG_PART_SEL.RIS, access to MPAMCFG_* should - be RAZ/WI (Read As Zero/ Write Ignored) */ - - uint32_t reg_value; - uint32_t data; - reg_value = val_mpam_mmr_read(msc_index, reg_offset); - /* Check if register returns RAZ */ - if (reg_value != 0x00) - return ACS_STATUS_FAIL; - - /* Check if register ignores write */ - data = BITFIELD_SET(PART_SEL_PARTID_SEL, 0x10) | - BITFIELD_SET(PART_SEL_RIS, (max_ris_index + 1)); - val_mpam_mmr_write(msc_index, reg_offset, data); - reg_value = val_mpam_mmr_read(msc_index, reg_offset); - if (reg_value != 0x00) - return ACS_STATUS_FAIL; - - return ACS_STATUS_PASS; -} - static void payload(void) { @@ -60,9 +46,14 @@ void payload(void) uint32_t status; uint32_t pe_index; uint32_t partid; + uint32_t internal; uint32_t total_nodes; uint32_t mpamf_ecr; + uint32_t selector_original; + uint32_t selector_readback; uint32_t esr_errcode; + uint32_t max_ris_index; + uint32_t undefined_ris; uint32_t data; uint32_t test_fail = 0; uint32_t test_skip = 1; @@ -70,21 +61,32 @@ void payload(void) pe_index = val_pe_get_index_mpid(val_pe_get_mpid()); total_nodes = val_mpam_get_msc_count(); + /* Validate undefined-RIS handling independently on every eligible MSC. */ for (msc_index = 0; msc_index < total_nodes; msc_index++) { - if (!val_mpam_msc_supports_esr(msc_index)) { + /* ESR support is required to observe a reported undefined-RIS error. */ + if (!val_mpam_msc_supports_esr(msc_index)) { val_print(DEBUG, "\n MSC index %d does not support ESR", msc_index); continue; } + /* The rule applies only when resource instance selection is supported. */ if (!val_mpam_msc_supports_ris(msc_index)) { val_print(DEBUG, "\n MSC index %d does not support RIS", msc_index); continue; } - /* Read MPAMF_ECR before generating error. This will be used to restore to default later */ + /* RIS_MAX + 1 must fit in the architectural selector field. */ + max_ris_index = val_mpam_get_max_ris_count(msc_index); + if (max_ris_index == BITFIELD_READ(PART_SEL_RIS, ACS_UINT32_MAX)) { + val_print(DEBUG, "\n MSC index %d has no undefined RIS value", msc_index); + continue; + } + + /* Preserve selector and error state before provoking the error condition. */ mpamf_ecr = val_mpam_mmr_read(msc_index, REG_MPAMF_ECR); + selector_original = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); status = val_mpam_msc_reset_errcode(msc_index); if (!status) { @@ -94,18 +96,20 @@ void payload(void) test_skip = 0; - /* Program MPAMCFG_PART_SEL with out-of-range RIS index */ - partid = val_mpam_get_max_partid(msc_index); - max_ris_index = val_mpam_get_max_ris_count(msc_index); + /* Keep PARTID valid so the programmed RIS is the only invalid selector. */ + internal = val_mpam_msc_supports_partid_nrw(msc_index); + partid = internal ? 0 : val_mpam_get_max_partid(msc_index); + undefined_ris = max_ris_index + 1U; data = BITFIELD_SET(PART_SEL_PARTID_SEL, partid) | - BITFIELD_SET(PART_SEL_RIS, (max_ris_index + 1)); + BITFIELD_SET(PART_SEL_RIS, undefined_ris) | + BITFIELD_SET(PART_SEL_INTERNAL, internal); val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, data); val_print(DEBUG, "\n Value written to MPAMCFG_PART_SEL is %llx", data); - /* Access MPAMCFG_* register to cause the error */ - val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + /* Read back the selector to determine whether the write was accepted. */ + selector_readback = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); /* Wait for some time for the error to be reflected in MPAMF_ESR */ val_time_delay_ms(100 * ONE_MILLISECOND); @@ -114,27 +118,32 @@ void payload(void) esr_errcode = val_mpam_msc_get_errcode(msc_index); val_print(DEBUG, "\n Error code read is %llx", esr_errcode); - status = check_for_raz_wi(msc_index, REG_MPAMCFG_PART_SEL); - if (status == ACS_STATUS_FAIL) { - val_print(ERROR, - "\n MPAMCFG_PART_SEL is not RAZ/WI with out-of-range RIS programmed"); + /* Classify the result against the two implementation-defined VKKDP behaviors. */ + if ((BITFIELD_READ(PART_SEL_RIS, selector_readback) == undefined_ris) && + (esr_errcode == ESR_ERRCODE_NO_ERROR)) { + val_print(DEBUG, "\n Undefined RIS accepted without checking"); + } else if ((selector_readback == selector_original) && + (esr_errcode == ESR_ERRCODE_UNDEF_RIS_PART_SEL)) { + val_print(DEBUG, "\n Undefined RIS rejected and error reported"); + } else { + val_print(ERROR, "\n Undefined RIS handling does not match a permitted behavior"); + val_print(ERROR, "\n Selector readback: 0x%x, errcode: %d", + selector_readback, esr_errcode); test_fail++; } - if ((esr_errcode != ESR_ERRCODE_UNDEF_RIS_PART_SEL) && - (esr_errcode != ESR_ERRCODE_NO_ERROR)) { - val_print(ERROR, "\n Expected errcode: %d or no error", - ESR_ERRCODE_UNDEF_RIS_PART_SEL); - val_print(ERROR, "\n Actual errcode: %d", esr_errcode); + /* Restore the selector and clear any error before testing the next MSC. */ + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, selector_original); + if (!val_mpam_msc_reset_errcode(msc_index)) { + val_print(ERROR, "\n Failed to clear MPAMF_ESR for MSC index %d", msc_index); test_fail++; - } else if (esr_errcode == ESR_ERRCODE_NO_ERROR) { - val_print(DEBUG, "\n Error not reported; permitted RAZ/WI behavior observed"); } - /* Restore Error Control Register original settings */ + /* Restore the original error-control configuration. */ val_mpam_mmr_write(msc_index, REG_MPAMF_ECR, mpamf_ecr); } + /* Report skip only when no MSC was eligible for this test. */ if (test_skip) val_set_status(pe_index, RESULT_SKIP(01)); else if (test_fail) diff --git a/test_pool/mpam/error009.c b/test_pool/mpam/error009.c index 28993eee..fbd5a13f 100644 --- a/test_pool/mpam/error009.c +++ b/test_pool/mpam/error009.c @@ -71,17 +71,16 @@ configure_partitioning_reg(uint32_t reg_offset) esr_errcode = val_mpam_msc_get_errcode(msc_index); val_print(DEBUG, "\n Error code read is %llx", esr_errcode); - status = check_for_raz_wi(reg_offset); - if (status == ACS_STATUS_FAIL) - val_print(ERROR, "\n Partitioning register is not RAZ/WI"); - - if ((esr_errcode != ESR_ERRCODE_RIS_NO_CTRL) && - (esr_errcode != ESR_ERRCODE_NO_ERROR)) { + if (esr_errcode == ESR_ERRCODE_NO_ERROR) { + status = check_for_raz_wi(reg_offset); + if (status == ACS_STATUS_FAIL) + val_print(ERROR, "\n Partitioning register is not RAZ/WI"); + else + val_print(DEBUG, "\n Error not reported; permitted RAZ/WI behavior observed"); + } else if (esr_errcode != ESR_ERRCODE_RIS_NO_CTRL) { val_print(ERROR, "\n Expected errcode: %d or no error", ESR_ERRCODE_RIS_NO_CTRL); val_print(ERROR, "\n Actual errcode: %d", esr_errcode); status = ACS_STATUS_FAIL; - } else if (esr_errcode == ESR_ERRCODE_NO_ERROR) { - val_print(DEBUG, "\n Error not reported; permitted RAZ/WI behavior observed"); } return status; diff --git a/test_pool/mpam/error010.c b/test_pool/mpam/error010.c index 134a3c85..fefe0093 100644 --- a/test_pool/mpam/error010.c +++ b/test_pool/mpam/error010.c @@ -21,37 +21,23 @@ #include "acs_mpam_reg.h" #include "val_interface.h" +/* + * Algorithm: + * 1. Iterate over MSCs that implement ESR, RIS, and monitor registers. + * 2. Save MPAMF_ECR and MSMON_CFG_MON_SEL, then clear MPAMF_ESR.ERRCODE. + * 3. Write RIS_MAX + 1 to MSMON_CFG_MON_SEL.RIS for monitor 0. + * 4. Read back MSMON_CFG_MON_SEL and MPAMF_ESR.ERRCODE. + * 5. Accept either behavior permitted by HJRZC: + * - the undefined RIS is retained without an immediate error, or + * - the selector is not updated and undefined-RIS error 10 is reported. + * 6. Restore the selector and error-control state and clear MPAMF_ESR. + * 7. Pass if every eligible MSC follows one of the permitted behaviors. + */ + #define TEST_NUM ACS_MPAM_ERROR_TEST_NUM_BASE + 10 #define TEST_DESC "Check Undef RIS MSMON_CFG_MON_SEL Err " #define TEST_RULE "HJRZC" -static uint32_t max_ris_index; - -static -uint32_t -check_for_raz_wi(uint32_t msc_index, uint32_t reg_offset) -{ - /* With out-of-range RIS value programmed to MSMON_CFG_MON_SEL.RIS, access to MSMON_CFG* should - be RAZ/WI (Read As Zero/ Write Ignored) */ - - uint32_t reg_value; - uint32_t data; - reg_value = val_mpam_mmr_read(msc_index, reg_offset); - /* Check if register returns RAZ */ - if (reg_value != 0x00) - return ACS_STATUS_FAIL; - - /* Check if register ignores write */ - data = BITFIELD_SET(MON_SEL_MON_SEL, 0x10) | - BITFIELD_SET(MON_SEL_RIS, (max_ris_index + 1)); - val_mpam_mmr_write(msc_index, reg_offset, data); - reg_value = val_mpam_mmr_read(msc_index, reg_offset); - if (reg_value != 0x00) - return ACS_STATUS_FAIL; - - return ACS_STATUS_PASS; -} - static void payload(void) { @@ -61,7 +47,11 @@ void payload(void) uint32_t pe_index; uint32_t total_nodes; uint32_t mpamf_ecr; + uint32_t selector_original; + uint32_t selector_readback; uint32_t esr_errcode; + uint32_t max_ris_index; + uint32_t undefined_ris; uint32_t data; uint32_t test_fail = 0; uint32_t test_skip = 1; @@ -69,28 +59,39 @@ void payload(void) pe_index = val_pe_get_index_mpid(val_pe_get_mpid()); total_nodes = val_mpam_get_msc_count(); + /* Validate undefined monitor-RIS handling on every eligible MSC. */ for (msc_index = 0; msc_index < total_nodes; msc_index++) { + /* ESR support is required to observe a reported undefined-RIS error. */ if (!val_mpam_msc_supports_esr(msc_index)) { val_print(DEBUG, "\n MSC index %d does not support ESR", msc_index); continue; } + /* The rule applies only when resource instance selection is supported. */ if (!val_mpam_msc_supports_ris(msc_index)) { val_print(DEBUG, "\n MSC index %d does not support RIS", msc_index); continue; } - /* Error cannot be generated if MSMON_CFG_MON_SEL register is not implemented */ + /* MSMON_CFG_MON_SEL must exist to exercise the monitor RIS selector. */ if (!val_mpam_msc_supports_mon(msc_index)) { val_print(DEBUG, "\n MSC index %d does not implement MSMON", msc_index); continue; } - /* Read MPAMF_ECR before generating error. This will be used to restore to default later */ + /* RIS_MAX + 1 must fit in the architectural selector field. */ + max_ris_index = val_mpam_get_max_ris_count(msc_index); + if (max_ris_index == BITFIELD_READ(MON_SEL_RIS, ACS_UINT32_MAX)) { + val_print(DEBUG, "\n MSC index %d has no undefined RIS value", msc_index); + continue; + } + + /* Preserve selector and error state before provoking the error condition. */ mpamf_ecr = val_mpam_mmr_read(msc_index, REG_MPAMF_ECR); + selector_original = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); status = val_mpam_msc_reset_errcode(msc_index); if (!status) { @@ -100,17 +101,17 @@ void payload(void) test_skip = 0; - /* Program MSMON_CFG_MON_SEL with out-of-range RIS index */ - max_ris_index = val_mpam_get_max_ris_count(msc_index); + /* Keep MON_SEL valid so RIS is the only invalid selector. */ + undefined_ris = max_ris_index + 1U; data = BITFIELD_SET(MON_SEL_MON_SEL, 0) | - BITFIELD_SET(MON_SEL_RIS, (max_ris_index + 1)); + BITFIELD_SET(MON_SEL_RIS, undefined_ris); val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, data); val_print(DEBUG, "\n Value written to MSMON_CFG_MON_SEL is %llx", data); - /* Access MSMON_CFG_* register to cause the error */ - val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + /* Read back the selector to determine whether the write was accepted. */ + selector_readback = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); /* Wait for some time for the error to be reflected in MPAMF_ESR */ val_time_delay_ms(100 * ONE_MILLISECOND); @@ -119,27 +120,32 @@ void payload(void) esr_errcode = val_mpam_msc_get_errcode(msc_index); val_print(DEBUG, "\n Error code read is %llx", esr_errcode); - status = check_for_raz_wi(msc_index, REG_MSMON_CFG_MON_SEL); - if (status == ACS_STATUS_FAIL) { - val_print(ERROR, - "\n MSMON_CFG_MON_SEL is not RAZ/WI with out-of-range RIS programmed"); + /* Classify the result against the two implementation-defined HJRZC behaviors. */ + if ((BITFIELD_READ(MON_SEL_RIS, selector_readback) == undefined_ris) && + (esr_errcode == ESR_ERRCODE_NO_ERROR)) { + val_print(DEBUG, "\n Undefined RIS accepted without checking"); + } else if ((selector_readback == selector_original) && + (esr_errcode == ESR_ERRCODE_UNDEF_RIS_MON_SEL)) { + val_print(DEBUG, "\n Undefined RIS rejected and error reported"); + } else { + val_print(ERROR, "\n Undefined RIS handling does not match a permitted behavior"); + val_print(ERROR, "\n Selector readback: 0x%x, errcode: %d", + selector_readback, esr_errcode); test_fail++; } - if ((esr_errcode != ESR_ERRCODE_UNDEF_RIS_MON_SEL) && - (esr_errcode != ESR_ERRCODE_NO_ERROR)) { - val_print(ERROR, "\n Expected errcode: %d or no error", - ESR_ERRCODE_UNDEF_RIS_MON_SEL); - val_print(ERROR, "\n Actual errcode: %d", esr_errcode); + /* Restore the selector and clear any error before testing the next MSC. */ + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, selector_original); + if (!val_mpam_msc_reset_errcode(msc_index)) { + val_print(ERROR, "\n Failed to clear MPAMF_ESR for MSC index %d", msc_index); test_fail++; - } else if (esr_errcode == ESR_ERRCODE_NO_ERROR) { - val_print(DEBUG, "\n Error not reported; permitted RAZ/WI behavior observed"); } - /* Restore Error Control Register original settings */ + /* Restore the original error-control configuration. */ val_mpam_mmr_write(msc_index, REG_MPAMF_ECR, mpamf_ecr); } + /* Report skip only when no MSC was eligible for this test. */ if (test_skip) val_set_status(pe_index, RESULT_SKIP(01)); else if (test_fail) diff --git a/test_pool/mpam/error011.c b/test_pool/mpam/error011.c index 5b49bff6..a755b6fd 100644 --- a/test_pool/mpam/error011.c +++ b/test_pool/mpam/error011.c @@ -71,17 +71,16 @@ configure_monitoring_reg(uint32_t reg_offset) esr_errcode = val_mpam_msc_get_errcode(msc_index); val_print(DEBUG, "\n Error code read is %llx", esr_errcode); - status = check_for_raz_wi(reg_offset); - if (status == ACS_STATUS_FAIL) - val_print(ERROR, "\n Monitoring register is not RAZ/WI"); - - if ((esr_errcode != ESR_ERRCODE_RIS_NO_MON) && - (esr_errcode != ESR_ERRCODE_NO_ERROR)) { + if (esr_errcode == ESR_ERRCODE_NO_ERROR) { + status = check_for_raz_wi(reg_offset); + if (status == ACS_STATUS_FAIL) + val_print(ERROR, "\n Monitoring register is not RAZ/WI"); + else + val_print(DEBUG, "\n Error not reported; permitted RAZ/WI behavior observed"); + } else if (esr_errcode != ESR_ERRCODE_RIS_NO_MON) { val_print(ERROR, "\n Expected errcode: %d or no error", ESR_ERRCODE_RIS_NO_MON); val_print(ERROR, "\n Actual errcode: %d", esr_errcode); status = ACS_STATUS_FAIL; - } else if (esr_errcode == ESR_ERRCODE_NO_ERROR) { - val_print(DEBUG, "\n Error not reported; permitted RAZ/WI behavior observed"); } return status; diff --git a/test_pool/mpam/error012.c b/test_pool/mpam/error012.c index a2f540cd..4832f1dc 100644 --- a/test_pool/mpam/error012.c +++ b/test_pool/mpam/error012.c @@ -21,6 +21,18 @@ #include "acs_mpam_reg.h" #include "val_interface.h" +/* + * Algorithm: + * 1. Iterate over MSCs that implement resource selection and error reporting. + * 2. Save the error-control and partition-selector registers, then clear ESR. + * 3. Program RIS_MAX + 1 as an undefined resource selector. + * 4. Read back the selector and continue only if the undefined RIS is retained. + * 5. Clear any error caused by programming the selector. + * 6. Read MPAMF_IDR and verify that all HAS_* fields are zero. + * 7. Verify that the ID-register access does not record an error. + * 8. Restore the selector, error status, and error-control register. + */ + #define TEST_NUM ACS_MPAM_ERROR_TEST_NUM_BASE + 15 #define TEST_DESC "Check MPAM IDR Zero for Undefined RIS " #define TEST_RULE "" @@ -39,6 +51,7 @@ void payload(void) uint32_t data; uint32_t data_original; uint32_t max_ris_index; + uint32_t undefined_ris; uint32_t test_fail = 0; uint32_t test_skip = 1; uint64_t idr_value = 0; @@ -46,21 +59,24 @@ void payload(void) pe_index = val_pe_get_index_mpid(val_pe_get_mpid()); total_nodes = val_mpam_get_msc_count(); + /* Validate ID-register behavior on every eligible MSC. */ for (msc_index = 0; msc_index < total_nodes; msc_index++) { val_print(DEBUG, "\n MSC index : %d", msc_index); + + /* Resource selection is required to create an undefined RIS state. */ if (!val_mpam_msc_supports_ris(msc_index)) { val_print(DEBUG, "\n MSC index %d does not support RIS", msc_index); continue; } - /* Check if Error Status Register Supported */ + /* Error reporting is required to verify that the ID-register access is error-free. */ if (!val_mpam_msc_supports_esr(msc_index)) { val_print(DEBUG, "\n MSC index %d does not support ESR/ECR", msc_index); continue; } - /* Read MPAMF_ECR before generating error. This will be used to restore to default later */ + /* Preserve error control and clear stale errors. */ mpamf_ecr = val_mpam_mmr_read(msc_index, REG_MPAMF_ECR); status = val_mpam_msc_reset_errcode(msc_index); @@ -70,44 +86,61 @@ void payload(void) return; } - test_skip = 0; - - /* Program MPAMCFG_PART_SEL with out-of-range RIS index */ + /* Determine the first undefined RIS value representable by the selector. */ partid = val_mpam_get_max_partid(msc_index); max_ris_index = val_mpam_get_max_ris_count(msc_index); + if (max_ris_index == BITFIELD_READ(PART_SEL_RIS, ACS_UINT32_MAX)) { + val_print(DEBUG, "\n MSC index %d has no undefined RIS value", msc_index); + val_mpam_mmr_write(msc_index, REG_MPAMF_ECR, mpamf_ecr); + continue; + } + + undefined_ris = max_ris_index + 1U; data = BITFIELD_SET(PART_SEL_PARTID_SEL, partid) | - BITFIELD_SET(PART_SEL_RIS, (max_ris_index + 1)); + BITFIELD_SET(PART_SEL_RIS, undefined_ris); - /* Save original value */ + /* Save the selector before programming the undefined RIS. */ data_original = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + /* Program and read back the undefined resource selector. */ val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, data); data = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); val_print(DEBUG, "\n Value Read Back from MPAMCFG_PART_SEL is %llx", data); - /* Wait for some time for the error to be reflected in MPAMF_ESR */ + /* Skip when the implementation does not retain the undefined RIS. */ + if (BITFIELD_READ(PART_SEL_RIS, data) != undefined_ris) { + val_print(DEBUG, "\n Undefined RIS was not retained for MSC index %d", msc_index); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, data_original); + val_mpam_msc_reset_errcode(msc_index); + val_mpam_mmr_write(msc_index, REG_MPAMF_ECR, mpamf_ecr); + continue; + } + + /* Allow selector-write error reporting to complete. */ val_time_delay_ms(100 * ONE_MILLISECOND); - /* Reset Errorcode which might have updated after setting out of range RIS */ + /* Clear any error caused by writing the selector. */ status = val_mpam_msc_reset_errcode(msc_index); if (!status) { val_print(DEBUG, "\n Error Code Reset Failed after RIS"); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, data_original); + val_mpam_mmr_write(msc_index, REG_MPAMF_ECR, mpamf_ecr); val_set_status(pe_index, RESULT_FAIL(02)); return; } - /* Access MPAMF ID register, all HAS_* fields read as 0 */ + test_skip = 0; + + /* Read the feature ID register under the retained undefined RIS. */ idr_value = val_mpam_mmr_read64(msc_index, REG_MPAMF_IDR); - /* Check All HAS_* Fields read as 0 */ + /* All feature-presence fields must read as zero. */ if (idr_value & MPAMF_IDR_HAS_MASK) { val_print(ERROR, "\n ID Register HAS_* Field Non Zero : %llx", idr_value); test_fail++; } - /* Restore */ - val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, data_original); - + /* The ID-register access must not record an error. */ esr_errcode = val_mpam_msc_get_errcode(msc_index); val_print(DEBUG, "\n Error code read is %llx", esr_errcode); @@ -116,11 +149,17 @@ void payload(void) val_print(ERROR, "\n Actual errcode: %d", esr_errcode); test_fail++; } - /* Restore Error Control Register original settings */ + + /* Restore selector and error state. */ + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, data_original); + val_mpam_msc_reset_errcode(msc_index); + + /* Restore the original error-control configuration. */ val_mpam_mmr_write(msc_index, REG_MPAMF_ECR, mpamf_ecr); } + /* Report the aggregate result from all eligible MSCs. */ if (test_skip) val_set_status(pe_index, RESULT_SKIP(01)); else if (test_fail) diff --git a/test_pool/mpam/error013.c b/test_pool/mpam/error013.c index 433f02ee..29431edd 100644 --- a/test_pool/mpam/error013.c +++ b/test_pool/mpam/error013.c @@ -25,6 +25,19 @@ #define TEST_DESC "Check MPAMF_ESR.RIS field correctness " #define TEST_RULE "QBKDG" +/* + * Test algorithm: + * 1. Iterate over MSCs that implement RIS selection, ESR, and extended ESR fields. + * 2. Save MPAMF_ECR, clear stale error status, and choose RIS_MAX + 1 as undefined RIS. + * 3. For MPAMCFG access, use a request PARTID without narrowing or an internal PARTID with it. + * 4. Program undefined RIS, access MPAMCFG, and read MPAMF_ESR. + * 5. For error 8 or 9, verify ESR.RIS. Accept no error, but reject unrelated errors. + * 6. Restore MPAMCFG_PART_SEL and clear the error status. + * 7. Program undefined RIS in MSMON_CFG_MON_SEL and access MSMON when monitors exist. + * 8. For error 10 or 11, verify ESR.RIS. Accept no error, but reject unrelated errors. + * 9. Restore all modified state and report the aggregate result. + */ + static void payload(void) { @@ -36,33 +49,40 @@ void payload(void) uint32_t mpamf_ecr; uint32_t data_original; uint64_t esr_value; + uint32_t esr_errcode; uint32_t programmed_ris; uint32_t max_ris_index; + uint32_t internal; uint32_t test_fail = 0; uint32_t test_skip = 1; uint32_t partid; + /* Get the executing PE and number of MSCs to inspect. */ pe_index = val_pe_get_index_mpid(val_pe_get_mpid()); total_nodes = val_mpam_get_msc_count(); + /* Validate ESR.RIS behavior independently on every eligible MSC. */ for (msc_index = 0; msc_index < total_nodes; msc_index++) { + /* RIS selection is required to create a misconfigured RIS value. */ if (!val_mpam_msc_supports_ris(msc_index)) { val_print(DEBUG, "\n MSC index %d does not support RIS", msc_index); continue; } + /* ESR support is required to observe the generated error. */ if (!val_mpam_msc_supports_esr(msc_index)) { val_print(DEBUG, "\n MSC index %d does not support ESR", msc_index); continue; } + /* The RIS syndrome field requires extended ESR support. */ if (!val_mpam_msc_supports_extd_esr(msc_index)) { val_print(DEBUG, "\n MSC index %d does not support EXTD_ESR", msc_index); continue; } - /* Save and reset error state */ + /* Preserve error control and clear stale error status. */ mpamf_ecr = val_mpam_mmr_read(msc_index, REG_MPAMF_ECR); status = val_mpam_msc_reset_errcode(msc_index); @@ -71,38 +91,47 @@ void payload(void) return; } - test_skip = 0; - + /* RIS_MAX + 1 must fit in the architectural RIS selector field. */ max_ris_index = val_mpam_get_max_ris_count(msc_index); - if (max_ris_index == 0xF) { - /* Maximum RIS Supported, Skip This MSC */ + if (max_ris_index == BITFIELD_READ(PART_SEL_RIS, ACS_UINT32_MAX)) { + /* All selector values are in range, so this MSC cannot be tested. */ val_print(DEBUG, "\n MSC index %d supports all RIS", msc_index); continue; } - /********* Case 1: MPAMCFG_PART_SEL *********/ - /* Save original value */ + test_skip = 0; + + /********* Case 1: MPAMCFG access *********/ + /* Preserve MPAMCFG_PART_SEL before programming the invalid selector. */ data_original = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + /* Use an internal PARTID for configuration only when narrowing is implemented. */ programmed_ris = max_ris_index + 1; - partid = val_mpam_get_max_partid(msc_index); + internal = val_mpam_msc_supports_partid_nrw(msc_index); + partid = internal ? 0 : val_mpam_get_max_partid(msc_index); + /* Keep PARTID valid so undefined RIS is the only invalid selector component. */ data = BITFIELD_SET(PART_SEL_PARTID_SEL, partid) | - BITFIELD_SET(PART_SEL_RIS, programmed_ris); + BITFIELD_SET(PART_SEL_RIS, programmed_ris) | + BITFIELD_SET(PART_SEL_INTERNAL, internal); + /* Program the undefined RIS for the MPAMCFG access. */ val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, data); val_print(DEBUG, "\n Value written to MPAMCFG_PART_SEL is %llx", data); - /* Access MPAMCFG_* register to cause the error */ + /* Access MPAMCFG to exercise optional undefined-RIS detection. */ val_mpam_mmr_read(msc_index, REG_MPAMCFG_CMAX); - /* Wait some time for error to be reflected in MPAMF_ESR */ + /* Allow a detected error to be reflected in MPAMF_ESR. */ val_time_delay_ms(100 * ONE_MILLISECOND); - /* Read Error Status Register, check if error code recorded */ + /* Validate ESR.RIS only for errors caused by a misconfigured MPAMCFG RIS. */ esr_value = val_mpam_mmr_read64(msc_index, REG_MPAMF_ESR); + esr_errcode = (esr_value >> ESR_ERRCODE_SHIFT) & ESR_ERRCODE_MASK; - if ((esr_value >> ESR_ERRCODE_SHIFT) & ESR_ERRCODE_MASK) { + if ((esr_errcode == ESR_ERRCODE_UNDEF_RIS_PART_SEL) || + (esr_errcode == ESR_ERRCODE_RIS_NO_CTRL)) { + /* A reported RIS error must identify the programmed MPAMCFG RIS. */ if (((esr_value >> ESR_RIS_SHIFT) & ESR_RIS_MASK) != programmed_ris) { val_print(ERROR, "\n ESR.RIS mismatch MPAMCFG access: expected 0x%x", programmed_ris); @@ -110,37 +139,48 @@ void payload(void) ((esr_value >> ESR_RIS_SHIFT) & ESR_RIS_MASK)); test_fail++; } + } else if (esr_errcode == ESR_ERRCODE_NO_ERROR) { + /* Error detection is optional for this access. */ + val_print(DEBUG, "\n Undefined RIS error not reported for MPAMCFG access"); } else { - /* Print Warning for Mismatch in Error Code */ - val_print(WARN, "\n ERRCODE mismatch MPAMCFG access: Received 0x%x", - ((esr_value >> ESR_ERRCODE_SHIFT) & ESR_ERRCODE_MASK)); + /* Reject unrelated errors instead of interpreting their RIS field. */ + val_print(ERROR, "\n Unexpected ERRCODE for MPAMCFG access: Received 0x%x", + esr_errcode); + test_fail++; } - /* Restore and Reset */ + /* Restore the configuration selector and clear this case error. */ val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, data_original); val_mpam_msc_reset_errcode(msc_index); - /********* Case 2: MSMON_CFG_MON_SEL *********/ + /********* Case 2: MSMON access *********/ + /* Test monitor RIS reporting only when monitoring is implemented. */ if (val_mpam_msc_supports_mon(msc_index)) { + /* Preserve the monitor selector before programming the invalid RIS. */ data_original = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); programmed_ris = max_ris_index + 1; + /* Select monitor zero and the same undefined RIS. */ data = BITFIELD_SET(MON_SEL_MON_SEL, 0) | BITFIELD_SET(MON_SEL_RIS, programmed_ris); + /* Program the undefined RIS for the MSMON access. */ val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, data); val_print(DEBUG, "\n Value written to MSMON_CFG_MON_SEL is %llx", data); - /* Access MSMON_CFG_* register to cause the error */ + /* Access MSMON to exercise optional undefined-RIS detection. */ val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MBWU_FLT); - /* Wait some time for error to be reflected in MPAMF_ESR */ + /* Allow a detected error to be reflected in MPAMF_ESR. */ val_time_delay_ms(100 * ONE_MILLISECOND); - /* Read Error Status Register, check if error code recorded */ + /* Validate ESR.RIS only for errors caused by a misconfigured monitor RIS. */ esr_value = val_mpam_mmr_read64(msc_index, REG_MPAMF_ESR); + esr_errcode = (esr_value >> ESR_ERRCODE_SHIFT) & ESR_ERRCODE_MASK; - if ((esr_value >> ESR_ERRCODE_SHIFT) & ESR_ERRCODE_MASK) { + if ((esr_errcode == ESR_ERRCODE_UNDEF_RIS_MON_SEL) || + (esr_errcode == ESR_ERRCODE_RIS_NO_MON)) { + /* A reported RIS error must identify the programmed monitor RIS. */ if (((esr_value >> ESR_RIS_SHIFT) & ESR_RIS_MASK) != programmed_ris) { val_print(ERROR, "\n ESR.RIS mismatch MSMON_CFG access: expected 0x%x", programmed_ris); @@ -148,21 +188,26 @@ void payload(void) ((esr_value >> ESR_RIS_SHIFT) & ESR_RIS_MASK)); test_fail++; } + } else if (esr_errcode == ESR_ERRCODE_NO_ERROR) { + /* Error detection is optional for this access. */ + val_print(DEBUG, "\n Undefined RIS error not reported for MSMON access"); } else { - /* Print Warning for Mismatch in Error Code */ - val_print(WARN, "\n ERRCODE mismatch MSMON_CFG access: Received 0x%x", - ((esr_value >> ESR_ERRCODE_SHIFT) & ESR_ERRCODE_MASK)); + /* Reject unrelated errors instead of interpreting their RIS field. */ + val_print(ERROR, "\n Unexpected ERRCODE for MSMON access: Received 0x%x", + esr_errcode); + test_fail++; } - /* Restore and Reset */ + /* Restore the monitor selector and clear this case error. */ val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, data_original); val_mpam_msc_reset_errcode(msc_index); } - /* Restore */ + /* Restore the original error-control configuration. */ val_mpam_mmr_write(msc_index, REG_MPAMF_ECR, mpamf_ecr); } + /* Report one aggregate result for all eligible MSCs. */ if (test_skip) val_set_status(pe_index, RESULT_SKIP(01)); else if (test_fail) @@ -176,15 +221,17 @@ uint32_t error013_entry(uint32_t num_pe) uint32_t status = ACS_STATUS_FAIL; + /* This test executes on one PE. */ num_pe = 1; val_log_context((char8_t *)__FILE__, (char8_t *)__func__, __LINE__); status = val_initialize_test(TEST_NUM, TEST_DESC, num_pe); + /* Run the payload unless execution policy skips this rule. */ if (status != ACS_STATUS_SKIP) val_run_test_payload(TEST_NUM, num_pe, payload, 0); - /* get the result from all PE and check for failure */ + /* Collect and report the test result. */ status = val_check_for_error(TEST_NUM, num_pe, TEST_RULE); val_report_status(0, ACS_END(TEST_NUM), NULL); diff --git a/test_pool/mpam/error014.c b/test_pool/mpam/error014.c index 3191aaeb..bd3a0e31 100644 --- a/test_pool/mpam/error014.c +++ b/test_pool/mpam/error014.c @@ -25,6 +25,16 @@ #define TEST_DESC "Check MPAM error overwrite behavior " #define TEST_RULE "" +/* + * Test algorithm: + * 1. Save MPAM2_EL2 and program valid default PARTID and PMG labels. + * 2. For each MSC with ESR support, save ECR and clear stale error status. + * 3. Generate a PARTID selector range error and verify ERRCODE=1 and OVRWR=0. + * 4. Select one implemented monitor type and generate a monitor configuration error. + * 5. Without clearing ESR, verify ERRCODE changes to the second error and OVRWR is set. + * 6. Restore MPAM2_EL2, clear ESR, restore ECR, and report the aggregate result. + */ + static void payload(void) { @@ -33,6 +43,15 @@ payload(void) uint32_t status; uint32_t mpamf_ecr; uint32_t esr_errcode; + uint32_t first_errcode; + uint32_t part_sel; + uint32_t mon_sel; + uint32_t mon_test_sel; + uint32_t mon_filter; + uint32_t use_csumon; + /* Track which per-MSC state must be restored on every exit path. */ + uint32_t part_sel_saved; + uint32_t mon_state_saved; uint64_t mpam2_el2_temp; uint32_t test_fail = 0; @@ -40,11 +59,21 @@ payload(void) uint32_t pe_index = val_pe_get_index_mpid(val_pe_get_mpid()); uint32_t msc_cnt = val_mpam_get_msc_count(); - /* Save MPAM2_EL2 to a temp storage */ + /* Save MPAM2_EL2 and prevent unrelated request-label errors during the test. */ mpam2_el2_temp = val_mpam_reg_read(MPAM2_EL2); + status = val_mpam_program_el2(0, DEFAULT_PMG); + if (status) { + val_print(ERROR, "\n Failed to program valid MPAM2_EL2 labels"); + val_mpam_reg_write(MPAM2_EL2, mpam2_el2_temp); + val_set_status(pe_index, RESULT_FAIL(01)); + return; + } for (msc_index = 0; msc_index < msc_cnt; msc_index++) { + /* No selector state has been saved for this MSC yet. */ + part_sel_saved = 0; + mon_state_saved = 0; /* Check if Error Status Register Supported */ if (!val_mpam_msc_supports_esr(msc_index)) { @@ -57,23 +86,23 @@ payload(void) status = val_mpam_msc_reset_errcode(msc_index); if (!status) { - val_set_status(pe_index, RESULT_FAIL(01)); - return; + test_fail++; + goto restore_msc_state; } mon_count = 0; - /* Read the number of monitors implemented in this MSC */ + use_csumon = 0; + /* Select one monitor type because CSU and MBWU instance ranges are independent. */ if (val_mpam_msc_supports_mon(msc_index)) { if (val_mpam_supports_csumon(msc_index)) { - mon_count += val_mpam_get_csumon_count(msc_index); + mon_count = val_mpam_get_csumon_count(msc_index); + use_csumon = 1; val_print(DEBUG, "\n MSC implements %d CSU Monitors", mon_count); - } - - if (val_mpam_msc_supports_mbwumon(msc_index)) { - mon_count += val_mpam_get_mbwumon_count(msc_index); + } else if (val_mpam_msc_supports_mbwumon(msc_index)) { + mon_count = val_mpam_get_mbwumon_count(msc_index); val_print(DEBUG, - "\n MSC implements %d MBWU Monitors", val_mpam_get_mbwumon_count(msc_index)); + "\n MSC implements %d MBWU Monitors", mon_count); } } @@ -81,22 +110,22 @@ payload(void) Skip MSC if mon not present */ if (mon_count == 0) { val_print(WARN, "\n Cannot generate second error. Skipping MSC"); - continue; + goto restore_msc_state; } /* Generate PARTID selection range error (program MPAMCFG_PART_SEL with out-of-range PID) */ + part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + part_sel_saved = 1; status = val_mpam_msc_generate_psr_error(msc_index); if (status == ACS_STATUS_SKIP) - continue; - - /* Test runs on atleast one MSC */ - test_skip = 0; + goto restore_msc_state; /* Wait for some time for the error to be reflected in MPAMF_ESR */ val_time_delay_ms(100 * ONE_MILLISECOND); /* Read Error Status Register and check if the error code is recorded */ esr_errcode = val_mpam_msc_get_errcode(msc_index); + first_errcode = esr_errcode; val_print(DEBUG, "\n Scenario 1 - Error code read is %d", esr_errcode); if (esr_errcode != ESR_ERRCODE_PARTID_SEL_RANGE) @@ -113,8 +142,21 @@ payload(void) test_fail++; } - /* Generate MSMON config ID out-of-range error */ - val_mpam_msc_generate_msmon_config_error(msc_index, mon_count); + /* Preserve the filter of the monitor that the error helper will modify. */ + mon_sel = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + mon_test_sel = BITFIELD_WRITE(mon_sel, MON_SEL_MON_SEL, (mon_count - 1)); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, mon_test_sel); + mon_filter = val_mpam_mmr_read(msc_index, use_csumon ? + REG_MSMON_CFG_CSU_FLT : REG_MSMON_CFG_MBWU_FLT); + mon_state_saved = 1; + + /* Generate MSMON config ID out-of-range error. */ + status = val_mpam_msc_generate_msmon_config_error(msc_index, mon_count); + if (status == ACS_STATUS_SKIP) + goto restore_msc_state; + + /* Test runs on at least one MSC only when both errors can be generated. */ + test_skip = 0; /* Wait for some time for the error to be reflected in MPAMF_ESR */ val_time_delay_ms(100 * ONE_MILLISECOND); @@ -123,14 +165,17 @@ payload(void) esr_errcode = val_mpam_msc_get_errcode(msc_index); val_print(DEBUG, "\n Scenario 2 - Error code read is %d", esr_errcode); - if (esr_errcode != ESR_ERRCODE_MSMONCFG_ID_RANGE) + if ((esr_errcode == ESR_ERRCODE_NO_ERROR) || (esr_errcode == first_errcode)) { - val_print(ERROR, - "\n Expected errcode: %d", ESR_ERRCODE_MSMONCFG_ID_RANGE); - val_print(ERROR, "\n Actual errcode: %d", esr_errcode); + val_print(ERROR, "\n ERRCODE was not updated by the second error"); test_fail++; } + /* Error classification is validated separately from overwrite behavior. */ + if (esr_errcode != ESR_ERRCODE_MSMONCFG_ID_RANGE) + val_print(WARN, "\n Monitor configuration stimulus reported errcode: %d", + esr_errcode); + /* Check OVRWR bit - should be 1 for second error */ if (val_mpam_msc_get_esr_ovrwr(msc_index) == 0) { @@ -138,18 +183,32 @@ payload(void) test_fail++; } - /* Restore MPAM2_EL2 settings */ - val_mpam_reg_write(MPAM2_EL2, mpam2_el2_temp); +restore_msc_state: + /* Restore every register saved before an error or skip path. */ + if (part_sel_saved) + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, part_sel); + if (mon_state_saved) { + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, mon_test_sel); + val_mpam_mmr_write(msc_index, use_csumon ? + REG_MSMON_CFG_CSU_FLT : REG_MSMON_CFG_MBWU_FLT, mon_filter); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, mon_sel); + } - /* Clear the error status amd restore the error control register */ - val_mpam_msc_reset_errcode(msc_index); + /* Clear error state so it cannot affect a later test. */ + if (!val_mpam_msc_reset_errcode(msc_index)) { + val_print(ERROR, "\n Failed to clear MPAMF_ESR during cleanup"); + test_fail++; + } val_mpam_mmr_write(msc_index, REG_MPAMF_ECR, mpamf_ecr); } - if (test_skip) - val_set_status(pe_index, RESULT_SKIP(01)); - else if (test_fail) + /* Restore the request labels after all MSCs have been tested. */ + val_mpam_reg_write(MPAM2_EL2, mpam2_el2_temp); + + if (test_fail) val_set_status(pe_index, RESULT_FAIL(02)); + else if (test_skip) + val_set_status(pe_index, RESULT_SKIP(01)); else val_set_status(pe_index, RESULT_PASS); diff --git a/test_pool/mpam/feat001.c b/test_pool/mpam/feat001.c index 77fe4309..3a62e334 100644 --- a/test_pool/mpam/feat001.c +++ b/test_pool/mpam/feat001.c @@ -18,6 +18,7 @@ #include "acs_val.h" #include "acs_pe.h" #include "acs_mpam.h" +#include "acs_mpam_reg.h" #include "acs_memory.h" #include "val_interface.h" @@ -29,14 +30,13 @@ 1. Check if the MSC supports PARTID enable/disable control by confirming MPAMF_IDR.HAS_ENDIS == 1. 2. Select a valid PARTID (e.g., PARTID_X) and identify a cache resource that supports portion-based partitioning. -3. Program PARTID_X with a defined partition share (e.g., 100%) using the cache's CPOR config. -4. Set MPAM2_EL2 to assign PARTID_X to the active PE or thread. -5. Perform a memory workload (e.g., memcpy or streaming load/store) large enough to utilize a - measurable portion of the cache. +3. Preserve the effective partition controls for PARTID_X and PARTID_Y. +4. Give both PARTIDs unrestricted cache controls and use separate cold working sets. +5. Run PARTID_X traffic that occupies approximately one LLC capacity. 6. Use the CSU monitor to measure the cache usage by PARTID_X. Record the CSU counter as Value A. 7. Disable PARTID_X by writing its ID to MPAMCFG_DIS.PARTID. Do not set the NFU bit. -8. Generate Traffic for PARTID_Y with CPOR (100%) -9. Use the CSU monitor to measure the cache usage by PARTID_X. Record the CSU counter as Value B. +8. Generate PARTID_Y traffic on a separate LLC-sized working set to evict PARTID_X lines. +9. Use the CSU monitor to measure the remaining PARTID_X usage as Value B. 10. Value B should be less than Value A and gradually decreasing. 11. Now, re-enable PARTID_X by writing it to MPAMCFG_EN.PARTID and Disable PARTID_Y 12. Re-run the same memory workload once more from the same PE. @@ -47,7 +47,7 @@ Value B < Value A: confirms that the disabled PARTID was deprioritized or blocke If both conditions hold, the test is passed. */ -#define BUFFER_SIZE 0x100000 /* 1MB */ +#define CACHE_PERCENTAGE 50U #define NUM_SCENARIOS 3 static void @@ -60,6 +60,14 @@ payload(void) void *src_buf; void *dest_buf; + void *src_buf_y; + void *dest_buf_y; + uint32_t page_size; + uint32_t num_pages; + uint64_t num_pages64; + uint64_t cache_size; + uint64_t buffer_size64; + uint32_t buffer_size; uint32_t end_count = 0; uint32_t test_fail = 0; uint16_t partid_x = 1; @@ -69,10 +77,22 @@ payload(void) uint64_t cache_identifier; uint64_t counter[NUM_SCENARIOS]; uint64_t saved_el2; - bool cpart_flag = 0; + uint32_t cpart_flag = 0; bool test_skip = 1; uint32_t status = 0; uint32_t num_mon = 0; + uint32_t saved_part_sel; + uint32_t saved_mon_sel; + uint16_t saved_map_x; + uint16_t saved_map_y; + bool mappings_saved; + bool saved_enable_x; + bool saved_enable_y; + MPAM_CSUMON_STATE saved_csumon; + bool csumon_state_saved; + MPAM_PARTITION_STATE saved_partition_x; + MPAM_PARTITION_STATE saved_partition_y; + bool partition_state_saved; /* Check if LLC is valid */ if (llc_idx == CACHE_TABLE_EMPTY) { @@ -89,6 +109,17 @@ payload(void) return; } + cache_size = val_cache_get_info(CACHE_SIZE, llc_idx); + buffer_size64 = ((cache_size / 100U) * CACHE_PERCENTAGE) + + (((cache_size % 100U) * CACHE_PERCENTAGE) / 100U); + if ((cache_size == INVALID_CACHE_INFO) || (buffer_size64 == 0U) || + (buffer_size64 > ACS_UINT32_MAX)) { + val_print(DEBUG, "\n Invalid LLC size, skipping test"); + val_set_status(index, RESULT_SKIP(2)); + return; + } + buffer_size = (uint32_t)buffer_size64; + /* Iterate through all available LLC MSCs and their resources */ for (msc_index = 0; msc_index < msc_cnt; msc_index++) { @@ -114,6 +145,10 @@ payload(void) != cache_identifier) continue; + /* Preserve selectors before the test selects a resource or PARTID. */ + saved_part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + saved_mon_sel = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + /* Select resource instance if RIS feature implemented */ if (val_mpam_msc_supports_ris(msc_index)) val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); @@ -121,12 +156,16 @@ payload(void) if (val_mpam_get_max_partid(msc_index) < partid_y) { val_print(DEBUG, "\n MSC %d does not support required PARTIDs, skipping MSC", msc_index); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); continue; } num_mon = 0; cpart_flag = 0; end_count = 0; + csumon_state_saved = false; + partition_state_saved = false; /* Check if MSC implements CSU monitors */ if (val_mpam_supports_csumon(msc_index)) @@ -135,6 +174,33 @@ payload(void) if (num_mon == 0) { val_print(DEBUG, "\n MSC %d does not have CSU monitors, skipping MSC", msc_index); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + continue; + } + + /* Preserve request-to-internal mappings before preparing test PARTIDs. */ + mappings_saved = false; + if (val_mpam_msc_supports_partid_nrw(msc_index)) { + saved_map_x = val_mpam_get_partid_mapping(msc_index, partid_x); + saved_map_y = val_mpam_get_partid_mapping(msc_index, partid_y); + mappings_saved = true; + } + /* Preserve request enable states before preparing the test PARTIDs. */ + saved_enable_x = val_mpam_msc_partid_is_enabled(msc_index, partid_x); + saved_enable_y = val_mpam_msc_partid_is_enabled(msc_index, partid_y); + + /* Give each request PARTID a distinct control PARTID when narrowing is supported. */ + if ((val_mpam_prepare_partid(msc_index, partid_x, 0) != ACS_STATUS_PASS) || + (val_mpam_prepare_partid(msc_index, partid_y, 1) != ACS_STATUS_PASS)) { + if (mappings_saved) { + val_mpam_set_partid_mapping(msc_index, partid_x, saved_map_x); + val_mpam_set_partid_mapping(msc_index, partid_y, saved_map_y); + } + (void)val_mpam_msc_endis_partid(msc_index, saved_enable_x, 0, partid_x); + (void)val_mpam_msc_endis_partid(msc_index, saved_enable_y, 0, partid_y); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); continue; } @@ -144,41 +210,120 @@ payload(void) cpart_flag = 1; val_print(DEBUG, "\n MSC %d supports CPOR partitioning", msc_index); - /* The test uses CPOR as cache partitioning scheme. Disable CCAP settings */ - val_mpam_configure_ccap(msc_index, partid_x, 0, 100); - val_mpam_configure_ccap(msc_index, partid_y, 0, 100); - } else if (val_mpam_supports_ccap(msc_index)) { cpart_flag = 2; val_print(DEBUG, "\n MSC %d supports CCAP partitioning", msc_index); - /* The test uses CCAP as cache partitioning scheme. Disable CPOR settings */ - val_mpam_configure_cpor(msc_index, partid_x, 100); - val_mpam_configure_cpor(msc_index, partid_y, 100); - } else { val_print(DEBUG, "\n MSC %d does not support CPOR/CCAP partitioning, skipping...", msc_index); + if (mappings_saved) { + val_mpam_set_partid_mapping(msc_index, partid_x, saved_map_x); + val_mpam_set_partid_mapping(msc_index, partid_y, saved_map_y); + } + (void)val_mpam_msc_endis_partid(msc_index, saved_enable_x, 0, partid_x); + (void)val_mpam_msc_endis_partid(msc_index, saved_enable_y, 0, partid_y); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); continue; } - /* Allocate memory for source and destination buffers */ - src_buf = (void *)val_aligned_alloc(MEM_ALIGN_4K, BUFFER_SIZE); - dest_buf = (void *)val_aligned_alloc(MEM_ALIGN_4K, BUFFER_SIZE); + /* Allocate page-aligned memory for source and destination buffers. */ + page_size = val_memory_page_size(); + if (page_size == 0U) { + val_print(ERROR, "\n Invalid memory page size"); + if (mappings_saved) { + val_mpam_set_partid_mapping(msc_index, partid_x, saved_map_x); + val_mpam_set_partid_mapping(msc_index, partid_y, saved_map_y); + } + (void)val_mpam_msc_endis_partid(msc_index, saved_enable_x, 0, partid_x); + (void)val_mpam_msc_endis_partid(msc_index, saved_enable_y, 0, partid_y); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + val_set_status(index, RESULT_FAIL(01)); + return; + } - if ((src_buf == NULL) || (dest_buf == NULL)) { + num_pages64 = (buffer_size / page_size) + + ((buffer_size % page_size) != 0U); + if ((num_pages64 == 0U) || (num_pages64 > ACS_UINT32_MAX) || + (num_pages64 > (~(uint64_t)0 / page_size)) || + ((num_pages64 * page_size) < buffer_size)) { + val_print(ERROR, "\n Invalid page count for workload buffers"); + if (mappings_saved) { + val_mpam_set_partid_mapping(msc_index, partid_x, saved_map_x); + val_mpam_set_partid_mapping(msc_index, partid_y, saved_map_y); + } + (void)val_mpam_msc_endis_partid(msc_index, saved_enable_x, 0, partid_x); + (void)val_mpam_msc_endis_partid(msc_index, saved_enable_y, 0, partid_y); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + val_set_status(index, RESULT_FAIL(01)); + return; + } + num_pages = (uint32_t)num_pages64; + src_buf = (void *)val_memory_alloc_pages(num_pages); + dest_buf = (void *)val_memory_alloc_pages(num_pages); + src_buf_y = (void *)val_memory_alloc_pages(num_pages); + dest_buf_y = (void *)val_memory_alloc_pages(num_pages); + + if ((src_buf == NULL) || (dest_buf == NULL) || + (src_buf_y == NULL) || (dest_buf_y == NULL)) { val_print(ERROR, "\n Mem allocation failed"); val_set_status(index, RESULT_FAIL(01)); + if (dest_buf_y != NULL) + val_memory_free_pages(dest_buf_y, num_pages); + if (src_buf_y != NULL) + val_memory_free_pages(src_buf_y, num_pages); if (dest_buf != NULL) - val_memory_free_aligned(dest_buf); + val_memory_free_pages(dest_buf, num_pages); if (src_buf != NULL) - val_memory_free_aligned(src_buf); + val_memory_free_pages(src_buf, num_pages); + if (mappings_saved) { + val_mpam_set_partid_mapping(msc_index, partid_x, saved_map_x); + val_mpam_set_partid_mapping(msc_index, partid_y, saved_map_y); + } + (void)val_mpam_msc_endis_partid(msc_index, saved_enable_x, 0, partid_x); + (void)val_mpam_msc_endis_partid(msc_index, saved_enable_y, 0, partid_y); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); return; } - /* Test runs on atleast one MSC */ + /* Preserve the PE labels and partition controls before changing them. */ + saved_el2 = val_mpam_reg_read(MPAM2_EL2); + if (!val_mpam_save_partition_state(msc_index, partid_x, + &saved_partition_x)) { + test_fail++; + goto cleanup; + } + if (!val_mpam_save_partition_state(msc_index, partid_y, + &saved_partition_y)) { + val_mpam_restore_partition_state(msc_index, &saved_partition_x); + test_fail++; + goto cleanup; + } + partition_state_saved = true; + + /* Keep the unused partitioning mechanism unrestricted. */ + if (cpart_flag == 1) { + val_mpam_configure_ccap(msc_index, partid_x, 0, 100); + val_mpam_configure_ccap(msc_index, partid_y, 0, 100); + } else { + val_mpam_configure_cpor(msc_index, partid_x, 100); + val_mpam_configure_cpor(msc_index, partid_y, 100); + } + + /* Initialize, then evict both working sets so measured traffic starts cold. */ + val_pe_cache_invalidate_range((uint64_t)src_buf, buffer_size); + val_pe_cache_invalidate_range((uint64_t)dest_buf, buffer_size); + val_pe_cache_invalidate_range((uint64_t)src_buf_y, buffer_size); + val_pe_cache_invalidate_range((uint64_t)dest_buf_y, buffer_size); + val_mem_issue_dsb(); + + /* Test runs on at least one MSC. */ test_skip = 0; /* Step 3: Program PARTID_X with 100% Cache partition control */ @@ -187,6 +332,10 @@ payload(void) else val_mpam_configure_ccap(msc_index, partid_x, 0, 100); + /* Preserve CSU monitor 0 before changing its programmable state. */ + val_mpam_save_csumon_state(msc_index, 0, &saved_csumon); + csumon_state_saved = true; + /* Configure CSU Monitor */ val_mpam_configure_csu_mon(msc_index, partid_x, DEFAULT_PMG, 0); @@ -200,20 +349,19 @@ payload(void) }; /* Step 4: Set MPAM2_EL2 to assign PARTID_X to the active PE or thread */ - saved_el2 = val_mpam_reg_read(MPAM2_EL2); - status = val_mpam_program_el2(partid_x, DEFAULT_PMG); if (status) { val_print(ERROR, "\n MPAM2_EL2 programming failed"); - /* Free the buffers to the heap manager */ - val_memory_free_aligned(src_buf); - val_memory_free_aligned(dest_buf); - val_set_status(index, RESULT_FAIL(02)); - return; + test_fail++; + goto cleanup; } /* Step 5: Start mem copy */ - val_memcpy(src_buf, dest_buf, BUFFER_SIZE); + val_memcpy(src_buf, dest_buf, buffer_size); + /* Complete the workload before restoring the PE labels used by ACS. */ + val_mem_issue_dsb(); + val_mpam_reg_write(MPAM2_EL2, saved_el2); + /* Wait for some time before the memcpy settles and counters update */ val_time_delay_ms(TIMEOUT_MEDIUM); @@ -235,10 +383,7 @@ payload(void) goto cleanup; } - /* Invalidate the caches before next scenario */ - val_pe_cache_invalidate_range((uint64_t)src_buf, BUFFER_SIZE); - val_pe_cache_invalidate_range((uint64_t)dest_buf, BUFFER_SIZE); - val_mem_issue_dsb(); + /* Keep PARTID_X resident so PARTID_Y creates real cache pressure. */ /* Step 7: Disable PARTID_X. MPAMCFG_DIS.PARTID = PARTID_X. Do not set NFU. We need the PARTID later */ @@ -261,15 +406,8 @@ payload(void) status = val_mpam_program_el2(partid_y, DEFAULT_PMG); if (status) { val_print(ERROR, "\n MPAM2_EL2 programming failed"); - /* Free the buffers to the heap manager */ - val_pe_cache_invalidate_range((uint64_t)src_buf, BUFFER_SIZE); - val_pe_cache_invalidate_range((uint64_t)dest_buf, BUFFER_SIZE); - val_mem_issue_dsb(); - - val_memory_free_aligned(src_buf); - val_memory_free_aligned(dest_buf); - val_set_status(index, RESULT_FAIL(03)); - return; + test_fail++; + goto cleanup; } /* Enable CSU Monitor */ @@ -281,7 +419,11 @@ payload(void) --nrdy_timeout; }; - val_memcpy(src_buf, dest_buf, BUFFER_SIZE); + val_memcpy(src_buf_y, dest_buf_y, buffer_size); + /* Complete the workload before restoring the PE labels used by ACS. */ + val_mem_issue_dsb(); + val_mpam_reg_write(MPAM2_EL2, saved_el2); + /* Wait for some time before the memcpy settles and counters update */ val_time_delay_ms(TIMEOUT_MEDIUM); @@ -305,8 +447,8 @@ payload(void) goto cleanup; } - val_pe_cache_invalidate_range((uint64_t)src_buf, BUFFER_SIZE); - val_pe_cache_invalidate_range((uint64_t)dest_buf, BUFFER_SIZE); + val_pe_cache_invalidate_range((uint64_t)src_buf, buffer_size); + val_pe_cache_invalidate_range((uint64_t)dest_buf, buffer_size); val_mem_issue_dsb(); /* Step 11: Enable PARTID_X and Disable PARTID_Y. H/w must remember PARTID_X config */ @@ -322,15 +464,8 @@ payload(void) status = val_mpam_program_el2(partid_x, DEFAULT_PMG); if (status) { val_print(ERROR, "\n MPAM2_EL2 programming failed"); - /* Free the buffers to the heap manager */ - val_pe_cache_invalidate_range((uint64_t)src_buf, BUFFER_SIZE); - val_pe_cache_invalidate_range((uint64_t)dest_buf, BUFFER_SIZE); - val_mem_issue_dsb(); - - val_memory_free_aligned(src_buf); - val_memory_free_aligned(dest_buf); - val_set_status(index, RESULT_FAIL(04)); - return; + test_fail++; + goto cleanup; } /* Reset CSU monitor counting PARTID_X */ @@ -345,7 +480,11 @@ payload(void) --nrdy_timeout; }; - val_memcpy(src_buf, dest_buf, BUFFER_SIZE); + val_memcpy(src_buf, dest_buf, buffer_size); + /* Complete the workload before restoring the PE labels used by ACS. */ + val_mem_issue_dsb(); + val_mpam_reg_write(MPAM2_EL2, saved_el2); + /* Wait for some time before the memcpy settles and counters update */ val_time_delay_ms(TIMEOUT_MEDIUM); @@ -368,23 +507,54 @@ payload(void) } cleanup: - /* Restore MPAM2_EL2 settings */ + /* Restore the PE labels before cleanup accesses memory or MPAM controls. */ val_mpam_reg_write(MPAM2_EL2, saved_el2); + /* Stop and restore the monitor only after its original state was saved. */ + if (csumon_state_saved) { + val_mpam_csumon_disable(msc_index); + val_mpam_restore_csumon_state(msc_index, &saved_csumon); + } + + if (partition_state_saved) { + val_mpam_restore_partition_state(msc_index, &saved_partition_x); + val_mpam_restore_partition_state(msc_index, &saved_partition_y); + } - /* Free the buffers to the heap manager */ - val_pe_cache_invalidate_range((uint64_t)src_buf, BUFFER_SIZE); - val_pe_cache_invalidate_range((uint64_t)dest_buf, BUFFER_SIZE); + /* Restore original narrowing mappings before restoring selectors. */ + if (mappings_saved) { + val_mpam_set_partid_mapping(msc_index, partid_x, saved_map_x); + val_mpam_set_partid_mapping(msc_index, partid_y, saved_map_y); + } + /* Restore each request PARTID to its original enable state. */ + status = val_mpam_msc_endis_partid(msc_index, saved_enable_x, 0, partid_x); + status |= val_mpam_msc_endis_partid(msc_index, saved_enable_y, 0, partid_y); + if (status) { + val_print(ERROR, "\n Failed to restore test PARTID enable state"); + test_fail++; + } + + /* Restore the selectors changed by test configuration. */ + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + + /* Evict and release both independent working sets. */ + val_pe_cache_invalidate_range((uint64_t)src_buf, buffer_size); + val_pe_cache_invalidate_range((uint64_t)dest_buf, buffer_size); + val_pe_cache_invalidate_range((uint64_t)src_buf_y, buffer_size); + val_pe_cache_invalidate_range((uint64_t)dest_buf_y, buffer_size); val_mem_issue_dsb(); - val_memory_free_aligned(src_buf); - val_memory_free_aligned(dest_buf); + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + val_memory_free_pages(src_buf_y, num_pages); + val_memory_free_pages(dest_buf_y, num_pages); } } - if (test_skip) - val_set_status(index, RESULT_SKIP(3)); - else if (test_fail) + if (test_fail) val_set_status(index, RESULT_FAIL(5)); + else if (test_skip) + val_set_status(index, RESULT_SKIP(3)); else val_set_status(index, RESULT_PASS); diff --git a/test_pool/mpam/mem001.c b/test_pool/mpam/mem001.c index ef9cd701..295df8ef 100644 --- a/test_pool/mpam/mem001.c +++ b/test_pool/mpam/mem001.c @@ -161,6 +161,10 @@ void payload(void) if (val_mpam_msc_supports_ris(msc_index)) val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); + /* Prepare the request PARTID for narrowed or non-narrowed controls. */ + if (val_mpam_prepare_partid(msc_index, minmax_partid, 0) != ACS_STATUS_PASS) + continue; + if (val_mpam_supports_cpor(msc_index)) { /* Disable CPOR partitioning for min(max(PARTID)) */ val_mpam_configure_cpor(msc_index, minmax_partid, 100); @@ -216,6 +220,10 @@ void payload(void) if (val_mpam_msc_supports_ris(msc_index)) val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); + /* Prepare the request PARTID for narrowed or non-narrowed controls. */ + if (val_mpam_prepare_partid(msc_index, minmax_partid, 0) != ACS_STATUS_PASS) + continue; + if (!val_mpam_msc_supports_mbwpbm(msc_index)) continue; diff --git a/test_pool/mpam/mem002.c b/test_pool/mpam/mem002.c index 590d4c27..f6f615e0 100644 --- a/test_pool/mpam/mem002.c +++ b/test_pool/mpam/mem002.c @@ -117,6 +117,10 @@ static void config_mpam_params(uint32_t mpam2_el2) if (val_mpam_msc_supports_ris(msc_index)) val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); + /* Prepare the request PARTID for narrowed or non-narrowed controls. */ + if (val_mpam_prepare_partid(msc_index, minmax_partid, 0) != ACS_STATUS_PASS) + continue; + if (val_mpam_supports_cpor(msc_index)) { /* Disable CPOR partitioning for min(max(PARTID)) */ val_mpam_configure_cpor(msc_index, minmax_partid, 100); @@ -311,6 +315,10 @@ payload_primary(void) if (val_mpam_msc_supports_ris(msc_index)) val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); + /* Prepare the request PARTID for narrowed or non-narrowed controls. */ + if (val_mpam_prepare_partid(msc_index, minmax_partid, 0) != ACS_STATUS_PASS) + continue; + if (!val_mpam_get_mbwumon_count(msc_index)) { val_print(DEBUG, "\n No MBWU Monitor found to validate the test. Skipping test"); diff --git a/test_pool/mpam/mem003.c b/test_pool/mpam/mem003.c index 68f1ce5b..2f559582 100644 --- a/test_pool/mpam/mem003.c +++ b/test_pool/mpam/mem003.c @@ -160,6 +160,10 @@ void payload(void) if (val_mpam_msc_supports_ris(msc_index)) val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); + /* Prepare the request PARTID for narrowed or non-narrowed controls. */ + if (val_mpam_prepare_partid(msc_index, minmax_partid, 0) != ACS_STATUS_PASS) + continue; + if (val_mpam_supports_cpor(msc_index)) { /* Disable CPOR partitioning for min(max(PARTID)) */ val_mpam_configure_cpor(msc_index, minmax_partid, 100); @@ -215,6 +219,10 @@ void payload(void) if (val_mpam_msc_supports_ris(msc_index)) val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); + /* Prepare the request PARTID for narrowed or non-narrowed controls. */ + if (val_mpam_prepare_partid(msc_index, minmax_partid, 0) != ACS_STATUS_PASS) + continue; + if (!val_mpam_msc_supports_mbw_max(msc_index)) continue; diff --git a/test_pool/mpam/monitor001.c b/test_pool/mpam/monitor001.c index 86d8f7ac..a51ffba7 100644 --- a/test_pool/mpam/monitor001.c +++ b/test_pool/mpam/monitor001.c @@ -40,8 +40,8 @@ static void payload(void) uint32_t rsrc_index; uint32_t llc_index; uint64_t cache_identifier; - uint32_t cache_maxsize; - uint32_t max_pmg; + uint32_t cache_maxsize = 0; + uint32_t max_pmg = DEFAULT_PMG_MAX; uint32_t test_partid = 7; //Selecting distinct partid uint32_t cpor_nodes = 0; uint32_t csumon_count = 0; @@ -52,11 +52,23 @@ static void payload(void) uint64_t buf_size; uint32_t page_size; uint32_t num_pages; + uint64_t num_pages64; uint64_t mpam2_el2 = 0; uint64_t nrdy_timeout; uint32_t storage_value1; uint32_t storage_value2; uint32_t index = val_pe_get_index_mpid(val_pe_get_mpid()); + uint32_t test_run = 0; + uint32_t test_fail = 0; + uint32_t saved_part_sel = 0; + uint32_t saved_mon_sel = 0; + uint16_t saved_mapping = 0; + uint32_t mapping_saved; + uint32_t restore_status; + bool saved_enable; + bool enable_state_saved; + MPAM_CSUMON_STATE saved_csumon; + MPAM_PARTITION_STATE saved_partition; /* Get the Index for LLC */ llc_index = val_cache_get_llc_index(); @@ -77,6 +89,9 @@ static void payload(void) /* Get MPAM related information for LLC */ for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { + saved_part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + saved_mon_sel = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + rsrc_node_cnt = val_mpam_get_info(MPAM_MSC_RSRC_COUNT, msc_index, 0); for (rsrc_index = 0; rsrc_index < rsrc_node_cnt; rsrc_index++) { @@ -94,7 +109,8 @@ static void payload(void) cache_maxsize = GET_MAX_VALUE(cache_maxsize, val_cache_get_info(CACHE_SIZE, llc_index)); - max_pmg = val_mpam_get_max_pmg(msc_index); + max_pmg = GET_MIN_VALUE(max_pmg, + val_mpam_get_max_pmg(msc_index)); if (val_mpam_supports_csumon(msc_index)) csumon_count = val_mpam_get_csumon_count(msc_index); @@ -103,6 +119,8 @@ static void payload(void) } test_partid = GET_MIN_VALUE(test_partid, val_mpam_get_max_partid(msc_index)); } + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); } val_print(DEBUG, "\n CPOR Nodes = %d", cpor_nodes); @@ -118,31 +136,8 @@ static void payload(void) return; } - /* Configure CPOR settings for nodes supporting CPOR */ - for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { - - rsrc_node_cnt = val_mpam_get_info(MPAM_MSC_RSRC_COUNT, msc_index, 0); - for (rsrc_index = 0; rsrc_index < rsrc_node_cnt; rsrc_index++) { - - if (val_mpam_get_info(MPAM_MSC_RSRC_TYPE, msc_index, rsrc_index) != MPAM_RSRC_TYPE_PE_CACHE) - continue; - - if (val_mpam_get_info(MPAM_MSC_RSRC_DESC1, msc_index, rsrc_index) != cache_identifier) - continue; - - /* Select resource instance if RIS feature implemented */ - if (val_mpam_msc_supports_ris(msc_index)) - val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); - - if (val_mpam_supports_cpor(msc_index)) - val_mpam_configure_cpor(msc_index, test_partid, PARTITION_PERCENTAGE); - - } - } - /* Create two PMG groups for PE traffic */ pmg1 = DEFAULT_PMG; - pmg2 = max_pmg; mpam2_el2 = val_mpam_reg_read(MPAM2_EL2); mpam2_el2_temp = mpam2_el2; @@ -155,18 +150,12 @@ static void payload(void) mpam2_el2 |= (((uint64_t)pmg1 << MPAMn_ELx_PMG_D_SHIFT) | ((uint64_t)test_partid << MPAMn_ELx_PARTID_D_SHIFT)); - val_mpam_reg_write(MPAM2_EL2, mpam2_el2); - /* visit each MSC node and check for Cache resources */ for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { rsrc_node_cnt = val_mpam_get_info(MPAM_MSC_RSRC_COUNT, msc_index, 0); for (rsrc_index = 0; rsrc_index < rsrc_node_cnt; rsrc_index++) { - /* Select resource instance if RIS feature implemented */ - if (val_mpam_msc_supports_ris(msc_index)) - val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); - /* Check whether resource node is a PE Cache */ if (val_mpam_get_info(MPAM_MSC_RSRC_TYPE, msc_index, rsrc_index) != MPAM_RSRC_TYPE_PE_CACHE) @@ -176,9 +165,24 @@ static void payload(void) if (val_mpam_get_info(MPAM_MSC_RSRC_DESC1, msc_index, rsrc_index) != cache_identifier) continue; - buf_size = cache_maxsize * CACHE_PERCENTAGE / 100 ; + buf_size = ((uint64_t)cache_maxsize * CACHE_PERCENTAGE) / 100U; page_size = val_memory_page_size(); - num_pages = (uint32_t)((buf_size + page_size - 1) / page_size); + if (page_size == 0U) { + val_print(ERROR, "\n Invalid memory page size"); + val_set_status(index, RESULT_FAIL(01)); + return; + } + + num_pages64 = (buf_size / page_size) + ((buf_size % page_size) != 0U); + /* Validate the allocator page count before calculating its byte capacity. */ + if ((num_pages64 == 0U) || (num_pages64 > ACS_UINT32_MAX) || + (num_pages64 > (~(uint64_t)0 / page_size)) || + ((num_pages64 * page_size) < buf_size)) { + val_print(ERROR, "\n Invalid page count for workload buffers"); + val_set_status(index, RESULT_FAIL(01)); + return; + } + num_pages = (uint32_t)num_pages64; /*Allocate memory for source and destination buffers */ src_buf = (void *)val_memory_alloc_pages(num_pages); @@ -196,6 +200,82 @@ static void payload(void) return; } + /* Preserve both selectors before selecting this resource instance. */ + saved_part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + saved_mon_sel = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + /* Select and validate this resource only after fallible allocation succeeds. */ + if (val_mpam_msc_supports_ris(msc_index)) + val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); + + if (!val_mpam_supports_cpor(msc_index) || + !val_mpam_supports_csumon(msc_index) || + (val_mpam_get_csumon_count(msc_index) == 0U) || + (val_mpam_get_max_pmg(msc_index) == DEFAULT_PMG)) { + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + continue; + } + + mapping_saved = 0; + if (val_mpam_msc_supports_partid_nrw(msc_index)) { + saved_mapping = val_mpam_get_partid_mapping(msc_index, test_partid); + mapping_saved = 1; + } + + /* Save the request PARTID enable state before preparation. */ + enable_state_saved = val_mpam_msc_supports_partid_endis(msc_index); + saved_enable = enable_state_saved ? + val_mpam_msc_partid_is_enabled(msc_index, test_partid) : true; + + /* Prepare controls only after both workload buffers are available. */ + if (val_mpam_prepare_partid(msc_index, test_partid, 0) != ACS_STATUS_PASS) { + + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + restore_status = 0; + if (enable_state_saved) + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + if (restore_status) + val_print(ERROR, "\n Failed to restore test PARTID enable state"); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + test_fail++; + continue; + } + + /* Use a distinct supported PMG for the negative filter scenario. */ + pmg2 = val_mpam_get_max_pmg(msc_index); + + /* Preserve the effective partition controls before changing CPOR. */ + if (!val_mpam_save_partition_state(msc_index, test_partid, &saved_partition)) { + test_fail++; + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + if (enable_state_saved && + val_mpam_msc_endis_partid(msc_index, saved_enable, 0, test_partid)) + test_fail++; + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + continue; + } + test_run = 1; + + if (val_mpam_supports_cpor(msc_index)) + val_mpam_configure_cpor(msc_index, test_partid, PARTITION_PERCENTAGE); + + /* Apply the test PARTID only while running this resource's workload. */ + val_mpam_reg_write(MPAM2_EL2, mpam2_el2); + + /* Preserve monitor 0 before changing its filter, control, or VALUE. */ + val_mpam_save_csumon_state(msc_index, 0, &saved_csumon); + /* Configure CSU monitors with PMG1 */ if (val_mpam_supports_csumon(msc_index)) val_mpam_configure_csu_mon(msc_index, test_partid, pmg1, 0); @@ -261,6 +341,20 @@ static void payload(void) /*Restore MPAM2_EL2 settings */ val_mpam_reg_write(MPAM2_EL2, mpam2_el2_temp); + val_mpam_restore_csumon_state(msc_index, &saved_csumon); + val_mpam_restore_partition_state(msc_index, &saved_partition); + + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + restore_status = 0; + if (enable_state_saved) + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + if (restore_status) + val_print(ERROR, "\n Failed to restore test PARTID enable state"); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + /*Free the buffers */ if (dest_buf != NULL) { val_pe_cache_invalidate_range((uint64_t)dest_buf, buf_size); @@ -279,6 +373,20 @@ static void payload(void) /*Restore MPAM2_EL2 settings */ val_mpam_reg_write(MPAM2_EL2, mpam2_el2_temp); + val_mpam_restore_csumon_state(msc_index, &saved_csumon); + val_mpam_restore_partition_state(msc_index, &saved_partition); + + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + restore_status = 0; + if (enable_state_saved) + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + if (restore_status) + test_fail++; + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + /*Free the buffers */ if (dest_buf != NULL) { val_pe_cache_invalidate_range((uint64_t)dest_buf, buf_size); @@ -293,7 +401,12 @@ static void payload(void) } } - val_set_status(index, RESULT_PASS); + if (test_fail) + val_set_status(index, RESULT_FAIL(03)); + else if (test_run) + val_set_status(index, RESULT_PASS); + else + val_set_status(index, RESULT_SKIP(04)); return; } diff --git a/test_pool/mpam/monitor002.c b/test_pool/mpam/monitor002.c index 7f5265d3..d7ea4ff6 100644 --- a/test_pool/mpam/monitor002.c +++ b/test_pool/mpam/monitor002.c @@ -40,8 +40,8 @@ static void payload(void) uint32_t rsrc_index; uint32_t llc_index; uint64_t cache_identifier; - uint32_t cache_maxsize; - uint32_t max_pmg; + uint32_t cache_maxsize = 0; + uint32_t max_pmg = DEFAULT_PMG_MAX; uint32_t test_partid = 8; //Selecting distinct partid uint32_t ccap_nodes = 0; uint32_t csumon_count = 0; @@ -52,11 +52,23 @@ static void payload(void) uint64_t buf_size; uint32_t page_size; uint32_t num_pages; + uint64_t num_pages64; uint64_t mpam2_el2 = 0; uint64_t nrdy_timeout; uint32_t storage_value1; uint32_t storage_value2; uint32_t index = val_pe_get_index_mpid(val_pe_get_mpid()); + uint32_t test_run = 0; + uint32_t test_fail = 0; + uint32_t saved_part_sel = 0; + uint32_t saved_mon_sel = 0; + uint16_t saved_mapping = 0; + uint32_t mapping_saved; + uint32_t restore_status; + bool saved_enable; + bool enable_state_saved; + MPAM_CSUMON_STATE saved_csumon; + MPAM_PARTITION_STATE saved_partition; /* Get the Index for LLC */ llc_index = val_cache_get_llc_index(); @@ -77,6 +89,9 @@ static void payload(void) /* Get MPAM related information for LLC */ for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { + saved_part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + saved_mon_sel = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + rsrc_node_cnt = val_mpam_get_info(MPAM_MSC_RSRC_COUNT, msc_index, 0); for (rsrc_index = 0; rsrc_index < rsrc_node_cnt; rsrc_index++) { @@ -94,7 +109,8 @@ static void payload(void) cache_maxsize = GET_MAX_VALUE(cache_maxsize, val_cache_get_info(CACHE_SIZE, llc_index)); - max_pmg = val_mpam_get_max_pmg(msc_index); + max_pmg = GET_MIN_VALUE(max_pmg, + val_mpam_get_max_pmg(msc_index)); if (val_mpam_supports_csumon(msc_index)) csumon_count = val_mpam_get_csumon_count(msc_index); @@ -103,6 +119,8 @@ static void payload(void) } test_partid = GET_MIN_VALUE(test_partid, val_mpam_get_max_partid(msc_index)); } + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); } val_print(DEBUG, "\n CCAP Nodes = %d", ccap_nodes); @@ -118,31 +136,8 @@ static void payload(void) return; } - /* Configure CCAP settings for nodes supporting CCAP */ - for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { - - rsrc_node_cnt = val_mpam_get_info(MPAM_MSC_RSRC_COUNT, msc_index, 0); - for (rsrc_index = 0; rsrc_index < rsrc_node_cnt; rsrc_index++) { - - if (val_mpam_get_info(MPAM_MSC_RSRC_TYPE, msc_index, rsrc_index) != MPAM_RSRC_TYPE_PE_CACHE) - continue; - - if (val_mpam_get_info(MPAM_MSC_RSRC_DESC1, msc_index, rsrc_index) != cache_identifier) - continue; - - /* Select resource instance if RIS feature implemented */ - if (val_mpam_msc_supports_ris(msc_index)) - val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); - - if (val_mpam_supports_ccap(msc_index)) - val_mpam_configure_ccap(msc_index, test_partid, SOFTLIMIT_DIS, PARTITION_PERCENTAGE); - - } - } - /* Create two PMG groups for PE traffic */ pmg1 = DEFAULT_PMG; - pmg2 = max_pmg; mpam2_el2 = val_mpam_reg_read(MPAM2_EL2); mpam2_el2_temp = mpam2_el2; @@ -155,18 +150,12 @@ static void payload(void) mpam2_el2 |= (((uint64_t)pmg1 << MPAMn_ELx_PMG_D_SHIFT) | ((uint64_t)test_partid << MPAMn_ELx_PARTID_D_SHIFT)); - val_mpam_reg_write(MPAM2_EL2, mpam2_el2); - /* visit each MSC node and check for Cache resources */ for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { rsrc_node_cnt = val_mpam_get_info(MPAM_MSC_RSRC_COUNT, msc_index, 0); for (rsrc_index = 0; rsrc_index < rsrc_node_cnt; rsrc_index++) { - /* Select resource instance if RIS feature implemented */ - if (val_mpam_msc_supports_ris(msc_index)) - val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); - /* Check whether resource node is a PE Cache */ if (val_mpam_get_info(MPAM_MSC_RSRC_TYPE, msc_index, rsrc_index) != MPAM_RSRC_TYPE_PE_CACHE) @@ -176,9 +165,24 @@ static void payload(void) if (val_mpam_get_info(MPAM_MSC_RSRC_DESC1, msc_index, rsrc_index) != cache_identifier) continue; - buf_size = cache_maxsize * CACHE_PERCENTAGE / 100 ; + buf_size = ((uint64_t)cache_maxsize * CACHE_PERCENTAGE) / 100U; page_size = val_memory_page_size(); - num_pages = (uint32_t)((buf_size + page_size - 1) / page_size); + if (page_size == 0U) { + val_print(ERROR, "\n Invalid memory page size"); + val_set_status(index, RESULT_FAIL(01)); + return; + } + + num_pages64 = (buf_size / page_size) + ((buf_size % page_size) != 0U); + /* Validate the allocator page count before calculating its byte capacity. */ + if ((num_pages64 == 0U) || (num_pages64 > ACS_UINT32_MAX) || + (num_pages64 > (~(uint64_t)0 / page_size)) || + ((num_pages64 * page_size) < buf_size)) { + val_print(ERROR, "\n Invalid page count for workload buffers"); + val_set_status(index, RESULT_FAIL(01)); + return; + } + num_pages = (uint32_t)num_pages64; /*Allocate memory for source and destination buffers */ src_buf = (void *)val_memory_alloc_pages(num_pages); @@ -189,21 +193,96 @@ static void payload(void) if ((src_buf == NULL) || (dest_buf == NULL)) { val_print(ERROR, "\n Mem allocation failed"); val_set_status(index, RESULT_FAIL(01)); - if (src_buf != NULL) + if (src_buf != NULL) { val_pe_cache_invalidate_range((uint64_t)src_buf, buf_size); - if (src_buf != NULL) val_mem_issue_dsb(); - if (src_buf != NULL) val_memory_free_pages(src_buf, num_pages); - if (dest_buf != NULL) + } + if (dest_buf != NULL) { val_pe_cache_invalidate_range((uint64_t)dest_buf, buf_size); - if (dest_buf != NULL) val_mem_issue_dsb(); - if (dest_buf != NULL) val_memory_free_pages(dest_buf, num_pages); + } return; } + /* Preserve both selectors before selecting this resource instance. */ + saved_part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + saved_mon_sel = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + /* Select and validate this resource only after fallible allocation succeeds. */ + if (val_mpam_msc_supports_ris(msc_index)) + val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); + + if (!val_mpam_supports_ccap(msc_index) || + !val_mpam_supports_csumon(msc_index) || + (val_mpam_get_csumon_count(msc_index) == 0U) || + (val_mpam_get_max_pmg(msc_index) == DEFAULT_PMG)) { + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + continue; + } + + mapping_saved = 0; + if (val_mpam_msc_supports_partid_nrw(msc_index)) { + saved_mapping = val_mpam_get_partid_mapping(msc_index, test_partid); + mapping_saved = 1; + } + + /* Save the request PARTID enable state before preparation. */ + enable_state_saved = val_mpam_msc_supports_partid_endis(msc_index); + saved_enable = enable_state_saved ? + val_mpam_msc_partid_is_enabled(msc_index, test_partid) : true; + + /* Prepare controls only after both workload buffers are available. */ + if (val_mpam_prepare_partid(msc_index, test_partid, 0) != ACS_STATUS_PASS) { + + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + restore_status = 0; + if (enable_state_saved) + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + if (restore_status) + val_print(ERROR, "\n Failed to restore test PARTID enable state"); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + test_fail++; + continue; + } + + /* Use a distinct supported PMG for the negative filter scenario. */ + pmg2 = val_mpam_get_max_pmg(msc_index); + + /* Preserve the effective partition controls before changing CCAP. */ + if (!val_mpam_save_partition_state(msc_index, test_partid, &saved_partition)) { + test_fail++; + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + if (enable_state_saved && + val_mpam_msc_endis_partid(msc_index, saved_enable, 0, test_partid)) + test_fail++; + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + continue; + } + test_run = 1; + + if (val_mpam_supports_ccap(msc_index)) + val_mpam_configure_ccap(msc_index, test_partid, + SOFTLIMIT_DIS, PARTITION_PERCENTAGE); + + /* Apply the test PARTID only while running this resource's workload. */ + val_mpam_reg_write(MPAM2_EL2, mpam2_el2); + + /* Preserve monitor 0 before changing its filter, control, or VALUE. */ + val_mpam_save_csumon_state(msc_index, 0, &saved_csumon); + /* Configure CSU monitors with PMG1 */ if (val_mpam_supports_csumon(msc_index)) val_mpam_configure_csu_mon(msc_index, test_partid, pmg1, 0); @@ -269,6 +348,20 @@ static void payload(void) /*Restore MPAM2_EL2 settings */ val_mpam_reg_write(MPAM2_EL2, mpam2_el2_temp); + val_mpam_restore_csumon_state(msc_index, &saved_csumon); + val_mpam_restore_partition_state(msc_index, &saved_partition); + + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + restore_status = 0; + if (enable_state_saved) + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + if (restore_status) + val_print(ERROR, "\n Failed to restore test PARTID enable state"); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + /*Free the buffers */ if (dest_buf != NULL) { val_pe_cache_invalidate_range((uint64_t)dest_buf, buf_size); @@ -287,6 +380,20 @@ static void payload(void) /*Restore MPAM2_EL2 settings */ val_mpam_reg_write(MPAM2_EL2, mpam2_el2_temp); + val_mpam_restore_csumon_state(msc_index, &saved_csumon); + val_mpam_restore_partition_state(msc_index, &saved_partition); + + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + restore_status = 0; + if (enable_state_saved) + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + if (restore_status) + test_fail++; + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + /*Free the buffers */ if (dest_buf != NULL) { val_pe_cache_invalidate_range((uint64_t)dest_buf, buf_size); @@ -301,7 +408,12 @@ static void payload(void) } } - val_set_status(index, RESULT_PASS); + if (test_fail) + val_set_status(index, RESULT_FAIL(03)); + else if (test_run) + val_set_status(index, RESULT_PASS); + else + val_set_status(index, RESULT_SKIP(04)); return; } diff --git a/test_pool/mpam/monitor003.c b/test_pool/mpam/monitor003.c index 390b10c2..d016a5cc 100644 --- a/test_pool/mpam/monitor003.c +++ b/test_pool/mpam/monitor003.c @@ -40,16 +40,18 @@ static void payload(void) uint32_t rsrc_index; uint32_t llc_index; uint64_t cache_identifier; - uint32_t cache_maxsize; + uint32_t cache_maxsize = 0; uint32_t cpor_nodes = 0; uint32_t csumon_count = 0; uint8_t partid1; uint8_t partid2; void *src_buf = 0; void *dest_buf = 0; - uint64_t buf_size; + uint32_t buf_size; + uint64_t buf_size64; uint32_t page_size; uint32_t num_pages; + uint64_t num_pages64; uint64_t mpam2_el2 = 0; uint64_t nrdy_timeout; uint32_t storage_value1; @@ -128,6 +130,10 @@ static void payload(void) if (val_mpam_msc_supports_ris(msc_index)) val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); + /* Prepare the request PARTID for narrowed or non-narrowed controls. */ + if (val_mpam_prepare_partid(msc_index, test_partid, 0) != ACS_STATUS_PASS) + continue; + if (val_mpam_supports_cpor(msc_index)) val_mpam_configure_cpor(msc_index, test_partid, PARTITION_PERCENTAGE); @@ -149,8 +155,6 @@ static void payload(void) mpam2_el2 |= (((uint64_t)DEFAULT_PMG << MPAMn_ELx_PMG_D_SHIFT) | ((uint64_t)partid1 << MPAMn_ELx_PARTID_D_SHIFT)); - val_mpam_reg_write(MPAM2_EL2, mpam2_el2); - /* visit each MSC node and check for Cache resources */ for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { rsrc_node_cnt = val_mpam_get_info(MPAM_MSC_RSRC_COUNT, msc_index, 0); @@ -170,9 +174,31 @@ static void payload(void) if (val_mpam_get_info(MPAM_MSC_RSRC_DESC1, msc_index, rsrc_index) != cache_identifier) continue; - buf_size = cache_maxsize * CACHE_PERCENTAGE / 100 ; + /* Calculate and validate the workload size before allocating buffers. */ + buf_size64 = ((uint64_t)cache_maxsize * CACHE_PERCENTAGE) / 100U; + if ((buf_size64 == 0U) || (buf_size64 > ACS_UINT32_MAX)) { + val_print(ERROR, "\n Invalid workload buffer size"); + val_set_status(index, RESULT_FAIL(01)); + return; + } + buf_size = (uint32_t)buf_size64; + page_size = val_memory_page_size(); - num_pages = (uint32_t)((buf_size + page_size - 1) / page_size); + if (page_size == 0U) { + val_print(ERROR, "\n Invalid memory page size"); + val_set_status(index, RESULT_FAIL(01)); + return; + } + + num_pages64 = (buf_size / page_size) + ((buf_size % page_size) != 0U); + if ((num_pages64 == 0U) || (num_pages64 > ACS_UINT32_MAX) || + (num_pages64 > (~(uint64_t)0 / page_size)) || + ((num_pages64 * page_size) < buf_size)) { + val_print(ERROR, "\n Invalid page count for workload buffers"); + val_set_status(index, RESULT_FAIL(01)); + return; + } + num_pages = (uint32_t)num_pages64; /*Allocate memory for source and destination buffers */ src_buf = (void *)val_memory_alloc_pages(num_pages); @@ -191,6 +217,9 @@ static void payload(void) } + /* Apply the test PARTID only while running this resource's workload. */ + val_mpam_reg_write(MPAM2_EL2, mpam2_el2); + /* Configure CSU monitors with partid1 */ if (val_mpam_supports_csumon(msc_index)) val_mpam_configure_csu_mon(msc_index, partid1, DEFAULT_PMG, 0); diff --git a/test_pool/mpam/monitor004.c b/test_pool/mpam/monitor004.c index 5af072b4..9dd62924 100644 --- a/test_pool/mpam/monitor004.c +++ b/test_pool/mpam/monitor004.c @@ -39,7 +39,7 @@ static void payload(void) uint32_t rsrc_index; uint32_t llc_index; uint64_t cache_identifier; - uint32_t cache_maxsize; + uint32_t cache_maxsize = 0; uint32_t ccap_nodes = 0; uint32_t csumon_count = 0; uint8_t partid1; @@ -49,12 +49,24 @@ static void payload(void) uint64_t buf_size; uint32_t page_size; uint32_t num_pages; + uint64_t num_pages64; uint64_t mpam2_el2 = 0; uint64_t nrdy_timeout; uint32_t storage_value1; uint32_t storage_value2; uint32_t index = val_pe_get_index_mpid(val_pe_get_mpid()); uint32_t test_partid = 11; //Selecting distinct partid + uint32_t test_run = 0; + uint32_t test_fail = 0; + uint32_t saved_part_sel = 0; + uint32_t saved_mon_sel = 0; + uint16_t saved_mapping = 0; + uint32_t mapping_saved; + uint32_t restore_status; + bool saved_enable; + bool enable_state_saved; + MPAM_CSUMON_STATE saved_csumon; + MPAM_PARTITION_STATE saved_partition; /* Get the Index for LLC */ llc_index = val_cache_get_llc_index(); @@ -75,6 +87,9 @@ static void payload(void) /* Get MPAM related information for LLC */ for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { + saved_part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + saved_mon_sel = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + rsrc_node_cnt = val_mpam_get_info(MPAM_MSC_RSRC_COUNT, msc_index, 0); for (rsrc_index = 0; rsrc_index < rsrc_node_cnt; rsrc_index++) { @@ -84,6 +99,12 @@ static void payload(void) if (val_mpam_get_info(MPAM_MSC_RSRC_DESC1, msc_index, rsrc_index) != cache_identifier) continue; + /* Two distinct request PARTIDs are required by this test. */ + if (val_mpam_get_max_partid(msc_index) < 1U) + continue; + test_partid = GET_MIN_VALUE(test_partid, + val_mpam_get_max_partid(msc_index) - 1U); + /* Select resource instance if RIS feature implemented */ if (val_mpam_msc_supports_ris(msc_index)) val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); @@ -98,6 +119,8 @@ static void payload(void) ccap_nodes++; } } + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); } val_print(DEBUG, "\n CCAP Nodes = %d", ccap_nodes); @@ -111,29 +134,6 @@ static void payload(void) return; } - /* Configure CCAP settings for nodes supporting CCAP */ - for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { - - rsrc_node_cnt = val_mpam_get_info(MPAM_MSC_RSRC_COUNT, msc_index, 0); - for (rsrc_index = 0; rsrc_index < rsrc_node_cnt; rsrc_index++) { - - if (val_mpam_get_info(MPAM_MSC_RSRC_TYPE, msc_index, rsrc_index) != MPAM_RSRC_TYPE_PE_CACHE) - continue; - - if (val_mpam_get_info(MPAM_MSC_RSRC_DESC1, msc_index, rsrc_index) != cache_identifier) - continue; - - /* Select resource instance if RIS feature implemented */ - if (val_mpam_msc_supports_ris(msc_index)) - val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); - - if (val_mpam_supports_ccap(msc_index)) - val_mpam_configure_ccap(msc_index, test_partid, - SOFTLIMIT_DIS, PARTITION_PERCENTAGE); - - } - } - /* Create two PARTID groups for PE traffic */ partid1 = test_partid; partid2 = test_partid + 1; @@ -149,18 +149,12 @@ static void payload(void) mpam2_el2 |= (((uint64_t)DEFAULT_PMG << MPAMn_ELx_PMG_D_SHIFT) | ((uint64_t)partid1 << MPAMn_ELx_PARTID_D_SHIFT)); - val_mpam_reg_write(MPAM2_EL2, mpam2_el2); - /* visit each MSC node and check for Cache resources */ for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { rsrc_node_cnt = val_mpam_get_info(MPAM_MSC_RSRC_COUNT, msc_index, 0); for (rsrc_index = 0; rsrc_index < rsrc_node_cnt; rsrc_index++) { - /* Select resource instance if RIS feature implemented */ - if (val_mpam_msc_supports_ris(msc_index)) - val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); - /* Check whether resource node is a PE Cache */ if (val_mpam_get_info(MPAM_MSC_RSRC_TYPE, msc_index, rsrc_index) != MPAM_RSRC_TYPE_PE_CACHE) @@ -170,9 +164,24 @@ static void payload(void) if (val_mpam_get_info(MPAM_MSC_RSRC_DESC1, msc_index, rsrc_index) != cache_identifier) continue; - buf_size = cache_maxsize * CACHE_PERCENTAGE / 100 ; + buf_size = ((uint64_t)cache_maxsize * CACHE_PERCENTAGE) / 100U; page_size = val_memory_page_size(); - num_pages = (uint32_t)((buf_size + page_size - 1) / page_size); + if (page_size == 0U) { + val_print(ERROR, "\n Invalid memory page size"); + val_set_status(index, RESULT_FAIL(01)); + return; + } + + num_pages64 = (buf_size / page_size) + ((buf_size % page_size) != 0U); + /* Validate the allocator page count before calculating its byte capacity. */ + if ((num_pages64 == 0U) || (num_pages64 > ACS_UINT32_MAX) || + (num_pages64 > (~(uint64_t)0 / page_size)) || + ((num_pages64 * page_size) < buf_size)) { + val_print(ERROR, "\n Invalid page count for workload buffers"); + val_set_status(index, RESULT_FAIL(01)); + return; + } + num_pages = (uint32_t)num_pages64; /*Allocate memory for source and destination buffers */ src_buf = (void *)val_memory_alloc_pages(num_pages); @@ -191,6 +200,79 @@ static void payload(void) } + /* Preserve both selectors before selecting this resource instance. */ + saved_part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + saved_mon_sel = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + /* Select and validate this resource only after fallible allocation succeeds. */ + if (val_mpam_msc_supports_ris(msc_index)) + val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); + + if (!val_mpam_supports_ccap(msc_index) || + !val_mpam_supports_csumon(msc_index) || + (val_mpam_get_csumon_count(msc_index) == 0U)) { + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + continue; + } + + mapping_saved = 0; + if (val_mpam_msc_supports_partid_nrw(msc_index)) { + saved_mapping = val_mpam_get_partid_mapping(msc_index, test_partid); + mapping_saved = 1; + } + + /* Save the request PARTID enable state before preparation. */ + enable_state_saved = val_mpam_msc_supports_partid_endis(msc_index); + saved_enable = enable_state_saved ? + val_mpam_msc_partid_is_enabled(msc_index, test_partid) : true; + + /* Prepare controls only after both workload buffers are available. */ + if (val_mpam_prepare_partid(msc_index, test_partid, 0) != ACS_STATUS_PASS) { + + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + restore_status = 0; + if (enable_state_saved) + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + if (restore_status) + val_print(ERROR, "\n Failed to restore test PARTID enable state"); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + test_fail++; + continue; + } + + /* Preserve the effective partition controls before changing CCAP. */ + if (!val_mpam_save_partition_state(msc_index, test_partid, &saved_partition)) { + test_fail++; + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + if (enable_state_saved && + val_mpam_msc_endis_partid(msc_index, saved_enable, 0, test_partid)) + test_fail++; + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + continue; + } + test_run = 1; + + if (val_mpam_supports_ccap(msc_index)) + val_mpam_configure_ccap(msc_index, test_partid, + SOFTLIMIT_DIS, PARTITION_PERCENTAGE); + + /* Apply the test PARTID only while running this resource's workload. */ + val_mpam_reg_write(MPAM2_EL2, mpam2_el2); + + /* Preserve monitor 0 before changing its filter, control, or VALUE. */ + val_mpam_save_csumon_state(msc_index, 0, &saved_csumon); + /* Configure CSU monitors with partid1 */ if (val_mpam_supports_csumon(msc_index)) val_mpam_configure_csu_mon(msc_index, partid1, DEFAULT_PMG, 0); @@ -256,6 +338,20 @@ static void payload(void) /*Restore MPAM2_EL2 settings */ val_mpam_reg_write(MPAM2_EL2, mpam2_el2_temp); + val_mpam_restore_csumon_state(msc_index, &saved_csumon); + val_mpam_restore_partition_state(msc_index, &saved_partition); + + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + restore_status = 0; + if (enable_state_saved) + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + if (restore_status) + val_print(ERROR, "\n Failed to restore test PARTID enable state"); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + /*Free the buffers */ if (dest_buf != NULL) { val_pe_cache_invalidate_range((uint64_t)dest_buf, buf_size); @@ -274,6 +370,20 @@ static void payload(void) /*Restore MPAM2_EL2 settings */ val_mpam_reg_write(MPAM2_EL2, mpam2_el2_temp); + val_mpam_restore_csumon_state(msc_index, &saved_csumon); + val_mpam_restore_partition_state(msc_index, &saved_partition); + + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + restore_status = 0; + if (enable_state_saved) + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + if (restore_status) + test_fail++; + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + /*Free the buffers */ if (dest_buf != NULL) { val_pe_cache_invalidate_range((uint64_t)dest_buf, buf_size); @@ -288,7 +398,12 @@ static void payload(void) } } - val_set_status(index, RESULT_PASS); + if (test_fail) + val_set_status(index, RESULT_FAIL(03)); + else if (test_run) + val_set_status(index, RESULT_PASS); + else + val_set_status(index, RESULT_SKIP(04)); return; } diff --git a/test_pool/mpam/monitor005.c b/test_pool/mpam/monitor005.c index 5730b021..0ff7f090 100644 --- a/test_pool/mpam/monitor005.c +++ b/test_pool/mpam/monitor005.c @@ -27,7 +27,6 @@ #define TEST_DESC "Check MSMON CTL Disable Behavior " #define BUFFER_SIZE 0x101000 /* 1MB */ -#define TEST_CSUMON_VALUE 0x00DEAD00 static void payload(void) @@ -52,6 +51,21 @@ payload(void) uint32_t num_mon = 0; uint32_t page_size; uint32_t num_pages; + uint32_t saved_csu_ctl; + uint32_t saved_csu_flt; + uint32_t saved_mon_sel; + uint32_t mon_sel; + uint32_t csu_ctl; + uint32_t csu_flt; + uint16_t test_flt_partid; + uint32_t saved_part_sel; + uint32_t saved_resource_mon_sel; + uint16_t saved_mapping; + uint32_t mapping_saved; + uint32_t restore_status; + bool saved_enable; + bool enable_state_saved; + MPAM_PARTITION_STATE saved_partition; /* Check if LLC is valid */ if (llc_idx == CACHE_TABLE_EMPTY) { @@ -68,6 +82,15 @@ payload(void) return; } + page_size = val_memory_page_size(); + if (page_size == 0U) { + val_print(ERROR, "\n Invalid memory page size"); + val_set_status(index, RESULT_FAIL(1)); + return; + } + num_pages = (BUFFER_SIZE / page_size) + + ((BUFFER_SIZE % page_size) != 0U); + /* Iterate through all MSCs and get the minmax PARTID -> the test PARTID */ for (msc_index = 0; msc_index < msc_cnt; msc_index++) { max_partid = val_mpam_get_max_partid(msc_index); @@ -101,6 +124,11 @@ payload(void) start_count = 0; end_count = 0; + /* Preserve selectors before selecting this resource instance. */ + saved_part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + saved_resource_mon_sel = val_mpam_mmr_read(msc_index, + REG_MSMON_CFG_MON_SEL); + /* Select resource instance if RIS feature implemented */ if (val_mpam_msc_supports_ris(msc_index)) val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); @@ -112,6 +140,48 @@ payload(void) if (num_mon == 0) { val_print(DEBUG, "\n MSC %d does not have CSU monitors, skipping MSC", msc_index); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, + saved_resource_mon_sel); + continue; + } + + mapping_saved = 0; + if (val_mpam_msc_supports_partid_nrw(msc_index)) { + saved_mapping = val_mpam_get_partid_mapping(msc_index, test_partid); + mapping_saved = 1; + } + + /* Preserve the request PARTID enable state before preparation. */ + enable_state_saved = val_mpam_msc_supports_partid_endis(msc_index); + saved_enable = enable_state_saved ? + val_mpam_msc_partid_is_enabled(msc_index, test_partid) : true; + + /* Prepare the request PARTID for narrowed or non-narrowed controls. */ + if (val_mpam_prepare_partid(msc_index, test_partid, 0) != ACS_STATUS_PASS) { + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + if (enable_state_saved) + (void)val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, + saved_resource_mon_sel); + continue; + } + + /* Preserve the effective partition controls before changing them. */ + if (!val_mpam_save_partition_state(msc_index, test_partid, + &saved_partition)) { + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + if (enable_state_saved) + (void)val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, + saved_resource_mon_sel); + test_fail++; continue; } @@ -122,9 +192,6 @@ payload(void) if (val_mpam_supports_ccap(msc_index)) val_mpam_configure_ccap(msc_index, test_partid, 0, 100); - page_size = val_memory_page_size(); - num_pages = (uint32_t)((BUFFER_SIZE + page_size - 1) / page_size); - /* Allocate memory for source and destination buffers */ src_buf = (void *)val_memory_alloc_pages(num_pages); dest_buf = (void *)val_memory_alloc_pages(num_pages); @@ -136,13 +203,60 @@ payload(void) val_memory_free_pages(src_buf, num_pages); if (dest_buf != NULL) val_memory_free_pages(dest_buf, num_pages); + val_mpam_restore_partition_state(msc_index, &saved_partition); + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + if (enable_state_saved) + (void)val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, + saved_resource_mon_sel); return; } /* Test runs on atleast one MSC */ test_skip = 0; - /* Configure CSU Monitor */ + /* Select monitor 0 and save its configuration. */ + saved_mon_sel = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + mon_sel = BITFIELD_WRITE(saved_mon_sel, MON_SEL_MON_SEL, 0); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, mon_sel); + saved_csu_ctl = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_CSU_CTL); + saved_csu_flt = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_CSU_FLT); + + /* Configure CSU Monitor. This leaves the selected monitor disabled. */ + val_mpam_configure_csu_mon(msc_index, test_partid, DEFAULT_PMG, 0); + + /* Change CTL fields and verify that CTL is writable while EN is zero. */ + csu_ctl = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_CSU_CTL); + csu_ctl = BITFIELD_WRITE(csu_ctl, CSU_CTL_EN, 0); + csu_ctl = BITFIELD_WRITE(csu_ctl, CSU_CTL_MATCH_PARTID, 0); + csu_ctl = BITFIELD_WRITE(csu_ctl, CSU_CTL_MATCH_PMG, 0); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_CSU_CTL, csu_ctl); + csu_ctl = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_CSU_CTL); + if ((BITFIELD_READ(CSU_CTL_EN, csu_ctl) != 0) || + (BITFIELD_READ(CSU_CTL_MATCH_PARTID, csu_ctl) != 0) || + (BITFIELD_READ(CSU_CTL_MATCH_PMG, csu_ctl) != 0)) { + val_print(ERROR, "\n CSU CTL update failed while monitor disabled"); + test_fail++; + } + + /* Change FLT fields and verify that FLT is writable while EN is zero. */ + max_partid = val_mpam_get_max_partid(msc_index); + test_flt_partid = (max_partid == 0U) ? test_partid : + ((test_partid == 0U) ? 1U : (test_partid - 1U)); + csu_flt = BITFIELD_SET(CSU_FLT_PARTID, test_flt_partid) | + BITFIELD_SET(CSU_FLT_PMG, DEFAULT_PMG); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_CSU_FLT, csu_flt); + csu_flt = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_CSU_FLT); + if ((BITFIELD_READ(CSU_FLT_PARTID, csu_flt) != test_flt_partid) || + (BITFIELD_READ(CSU_FLT_PMG, csu_flt) != DEFAULT_PMG)) { + val_print(ERROR, "\n CSU FLT update failed while monitor disabled"); + test_fail++; + } + + /* Restore the disabled monitor setup used by the workload check. */ val_mpam_configure_csu_mon(msc_index, test_partid, DEFAULT_PMG, 0); /* Wait for MAX_NRDY_USEC after monitor config change */ @@ -158,11 +272,34 @@ payload(void) status = val_mpam_program_el2(test_partid, DEFAULT_PMG); if (status) { val_print(ERROR, "\n MPAM2_EL2 programming failed"); + /* Restore the selected monitor before leaving on setup failure. */ + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_CSU_FLT, saved_csu_flt); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_CSU_CTL, saved_csu_ctl); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + val_mpam_reg_write(MPAM2_EL2, saved_el2); + val_mpam_restore_partition_state(msc_index, &saved_partition); + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + restore_status = 0; + if (enable_state_saved) + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + if (restore_status) + val_print(ERROR, "\n Failed to restore test PARTID enable state"); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, + saved_resource_mon_sel); /* Free the buffers to the heap manager */ - if (dest_buf != NULL) + if (dest_buf != NULL) { + val_pe_cache_invalidate_range((uint64_t)dest_buf, BUFFER_SIZE); + val_mem_issue_dsb(); val_memory_free_pages(dest_buf, num_pages); - if (src_buf != NULL) + } + if (src_buf != NULL) { + val_pe_cache_invalidate_range((uint64_t)src_buf, BUFFER_SIZE); + val_mem_issue_dsb(); val_memory_free_pages(src_buf, num_pages); + } val_set_status(index, RESULT_FAIL(02)); return; } @@ -191,26 +328,28 @@ payload(void) test_fail++; } - /* If CSU Monitor is not Read Only, then check CSUMON Value is Writable */ - if (val_mpam_write_csumon(msc_index, TEST_CSUMON_VALUE) == 0) { - /* Check that value is updated */ - end_count = val_mpam_read_csumon(msc_index); - - if (TEST_CSUMON_VALUE != end_count) { - /* Fail the test */ - val_print(ERROR, "\n Unexpected CSUMON Value after Write"); - val_print(ERROR, "\n Expected = 0x%lx", TEST_CSUMON_VALUE); - val_print(ERROR, "\n Found = 0x%lx", end_count); - test_fail++; - } - } else { - /* CSU is Read Only, Skipping CSUMON Value RW Check */ - val_print(DEBUG, "\n CSU Read Only, Skipping Write Check"); - } + /* Restore the selected monitor configuration. */ + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_CSU_FLT, saved_csu_flt); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_CSU_CTL, saved_csu_ctl); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); /* Restore MPAM2_EL2 settings */ val_mpam_reg_write(MPAM2_EL2, saved_el2); + /* Restore partition controls, mapping, enable state, and selectors. */ + val_mpam_restore_partition_state(msc_index, &saved_partition); + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + restore_status = 0; + if (enable_state_saved) + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + if (restore_status) + test_fail++; + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, + saved_resource_mon_sel); + /* Free the buffers to the heap manager */ if (dest_buf != NULL) { val_pe_cache_invalidate_range((uint64_t)dest_buf, BUFFER_SIZE); @@ -225,10 +364,10 @@ payload(void) } } - if (test_skip) - val_set_status(index, RESULT_SKIP(3)); - else if (test_fail) + if (test_fail) val_set_status(index, RESULT_FAIL(3)); + else if (test_skip) + val_set_status(index, RESULT_SKIP(3)); else val_set_status(index, RESULT_PASS); diff --git a/test_pool/mpam/mpam006.c b/test_pool/mpam/mpam006.c index 7f29b1ef..529cdad3 100644 --- a/test_pool/mpam/mpam006.c +++ b/test_pool/mpam/mpam006.c @@ -52,6 +52,9 @@ program_all_monitors_with_pmg(uint16_t partid, uint8_t pmg) val_print(DEBUG, "\n Resource count = %d", rsrc_node_cnt); for (rsrc_index = 0; rsrc_index < rsrc_node_cnt; rsrc_index++) { + /* Capabilities are specific to the currently selected resource. */ + max_pmg = 0; + csumon_count = 0; /* Check whether resource node is a PE Cache */ if (val_mpam_get_info(MPAM_MSC_RSRC_TYPE, msc_index, rsrc_index) == @@ -65,13 +68,19 @@ program_all_monitors_with_pmg(uint16_t partid, uint8_t pmg) if (val_mpam_msc_supports_ris(msc_index)) val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); + /* Prepare the request PARTID for narrowed or non-narrowed controls. */ + if (val_mpam_prepare_partid(msc_index, partid, 0) != ACS_STATUS_PASS) + continue; + if (val_mpam_supports_cpor(msc_index)) { max_pmg = val_mpam_get_max_pmg(msc_index); val_mpam_configure_cpor(msc_index, partid, PARTITION_PERCENTAGE); - if (val_mpam_supports_csumon(msc_index)) - csumon_count = val_mpam_get_csumon_count(msc_index); + if (!val_mpam_supports_csumon(msc_index)) + continue; + + csumon_count = val_mpam_get_csumon_count(msc_index); } else continue; @@ -108,6 +117,8 @@ set_status_for_all_csumon(uint32_t status) val_print(DEBUG, "\n Resource count = %d ", rsrc_node_cnt); for (rsrc_index = 0; rsrc_index < rsrc_node_cnt; rsrc_index++) { + /* Do not reuse monitor capability state from another resource. */ + csumon_count = 0; /* Check whether resource node is a PE Cache */ if (val_mpam_get_info(MPAM_MSC_RSRC_TYPE, msc_index, rsrc_index) == @@ -123,8 +134,10 @@ set_status_for_all_csumon(uint32_t status) if (val_mpam_supports_cpor(msc_index)) { - if (val_mpam_supports_csumon(msc_index)) - csumon_count = val_mpam_get_csumon_count(msc_index); + if (!val_mpam_supports_csumon(msc_index)) + continue; + + csumon_count = val_mpam_get_csumon_count(msc_index); } else continue; @@ -164,6 +177,8 @@ read_all_msc_csu_counters(uint32_t expected_count) rsrc_node_cnt = val_mpam_get_info(MPAM_MSC_RSRC_COUNT, msc_index, 0); for (rsrc_index = 0; rsrc_index < rsrc_node_cnt; rsrc_index++) { + /* Do not reuse monitor capability state from another resource. */ + csumon_count = 0; /* Check whether resource node is a PE Cache */ if (val_mpam_get_info(MPAM_MSC_RSRC_TYPE, msc_index, rsrc_index) == @@ -179,8 +194,10 @@ read_all_msc_csu_counters(uint32_t expected_count) if (val_mpam_supports_cpor(msc_index)) { - if (val_mpam_supports_csumon(msc_index)) - csumon_count = val_mpam_get_csumon_count(msc_index); + if (!val_mpam_supports_csumon(msc_index)) + continue; + + csumon_count = val_mpam_get_csumon_count(msc_index); } else continue; diff --git a/test_pool/mpam/partition001.c b/test_pool/mpam/partition001.c index ad767c29..1ff4dbd5 100644 --- a/test_pool/mpam/partition001.c +++ b/test_pool/mpam/partition001.c @@ -39,9 +39,23 @@ typedef struct { static cpor_config_t cpor_config_data[] = { {"CPOR check for 75% partition size", 75, 75, true}, - {"CPOR check for 25% partition size", 25, 75, true} + {"CPOR check for 25% partition size", 25, 25, true} }; +static void free_counter_buffer(uint64_t **counter, uint32_t row_count) +{ + uint32_t row; + + if (counter == NULL) + return; + + for (row = 0; row < row_count; row++) { + if (counter[row] != NULL) + val_memory_free(counter[row]); + } + val_memory_free(counter); +} + static void payload(void) { uint8_t enabled_scenarios = 0; @@ -54,6 +68,7 @@ static void payload(void) uint64_t buf_size; uint32_t page_size; uint32_t num_pages; + uint64_t num_pages64; uint64_t nrdy_timeout; uint64_t **counter; uint64_t it; @@ -64,12 +79,36 @@ static void payload(void) uint32_t rsrc_index; uint32_t llc_index; uint64_t cache_identifier; - uint32_t cache_maxsize; + uint32_t cache_maxsize = 0; uint32_t test_partid = 1; //Selecting distinct partid + uint32_t max_scenario_index = 0; + uint32_t required_max_partid; + uint32_t counter_row_size; + uint64_t counter_row_size64; + uint32_t saved_int_partid; + uint32_t saved_part_sel; + uint32_t saved_mon_sel; + uint32_t narrowing_active; + uint32_t restore_status; + bool saved_enable; + bool enable_state_saved; + MPAM_PARTITION_STATE saved_partition; + MPAM_CSUMON_STATE saved_csumon; uint32_t csumon_count = 0; + uint32_t resource_slot; + uint32_t current_slot; uint32_t index = val_pe_get_index_mpid(val_pe_get_mpid()); uint32_t test_fail = 0; + /* Determine the largest request and internal PARTID needed by enabled scenarios. */ + for (cfg_index = 0; + cfg_index < sizeof(cpor_config_data) / sizeof(cpor_config_t); + cfg_index++) { + if (cpor_config_data[cfg_index].config_enable) + max_scenario_index = cfg_index; + } + required_max_partid = test_partid + max_scenario_index; + /* Get the Index for LLC */ llc_index = val_cache_get_llc_index(); if (llc_index == CACHE_TABLE_EMPTY) { @@ -89,6 +128,9 @@ static void payload(void) /* Get MPAM related information for LLC */ for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { + saved_part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + saved_mon_sel = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + rsrc_node_cnt = val_mpam_get_info(MPAM_MSC_RSRC_COUNT, msc_index, 0); for (rsrc_index = 0; rsrc_index < rsrc_node_cnt; rsrc_index++) { @@ -98,17 +140,27 @@ static void payload(void) if (val_mpam_get_info(MPAM_MSC_RSRC_DESC1, msc_index, rsrc_index) != cache_identifier) continue; - if (val_mpam_supports_cpor(msc_index)) { - cache_maxsize = GET_MAX_VALUE(cache_maxsize, - val_cache_get_info(CACHE_SIZE, llc_index)); + /* Select the resource before checking its capabilities. */ + if (val_mpam_msc_supports_ris(msc_index)) + val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); - if (val_mpam_supports_csumon(msc_index)) - csumon_count = val_mpam_get_csumon_count(msc_index); + if ((val_mpam_get_max_partid(msc_index) < required_max_partid) || + (val_mpam_msc_supports_partid_nrw(msc_index) && + (val_mpam_get_max_intpartid(msc_index) < max_scenario_index))) + continue; + if (val_mpam_supports_cpor(msc_index) && + val_mpam_supports_csumon(msc_index) && + (val_mpam_get_csumon_count(msc_index) != 0U)) { + cache_maxsize = GET_MAX_VALUE(cache_maxsize, + val_cache_get_info(CACHE_SIZE, llc_index)); + csumon_count = val_mpam_get_csumon_count(msc_index); cpor_nodes++; } - test_partid = GET_MIN_VALUE(test_partid, val_mpam_get_max_partid(msc_index)); } + + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); } val_print(DEBUG, "\n CPOR Nodes = %d", cpor_nodes); @@ -122,12 +174,32 @@ static void payload(void) return; } + /* Both allocation and initialization APIs accept a 32-bit byte count. */ + counter_row_size64 = (uint64_t)cpor_nodes * sizeof(uint64_t); + if ((counter_row_size64 == 0U) || (counter_row_size64 > ACS_UINT32_MAX)) { + val_print(ERROR, "\n Counter row size exceeds allocator limit"); + val_set_status(index, RESULT_FAIL(01)); + return; + } + counter_row_size = (uint32_t)counter_row_size64; + /* Dynamically create the CSU Mon Counter buffer * uint64_t counter[CPOR_SCENARIO_MAX][cpor_nodes] */ counter = (uint64_t **) val_memory_alloc(CPOR_SCENARIO_MAX * sizeof (uint64_t *)); + if (counter == NULL) { + val_set_status(index, RESULT_FAIL(01)); + return; + } + val_memory_set(counter, CPOR_SCENARIO_MAX * sizeof(uint64_t *), 0); for (it = 0; it < CPOR_SCENARIO_MAX; it++) { - counter[it] = val_memory_alloc(msc_node_cnt * sizeof (uint64_t)); + counter[it] = val_memory_alloc(counter_row_size); + if (counter[it] == NULL) { + free_counter_buffer(counter, CPOR_SCENARIO_MAX); + val_set_status(index, RESULT_FAIL(01)); + return; + } + val_memory_set(counter[it], counter_row_size, 0); } mpam2_el2 = val_mpam_reg_read(MPAM2_EL2); @@ -135,7 +207,8 @@ static void payload(void) for (cfg_index = 0; cfg_index < sizeof(cpor_config_data)/sizeof(cpor_config_t); cfg_index++) { - test_partid = test_partid + cfg_index; + /* Use one distinct request PARTID per enabled scenario. */ + test_partid = 1U + cfg_index; /* Clear the PARTID_D & PMG_D bits in mpam2_el2 before writing to them */ mpam2_el2 = CLEAR_BITS_M_TO_N(mpam2_el2, MPAMn_ELx_PARTID_D_SHIFT+15, MPAMn_ELx_PARTID_D_SHIFT); @@ -146,9 +219,15 @@ static void payload(void) mpam2_el2 |= (((uint64_t)DEFAULT_PMG << MPAMn_ELx_PMG_D_SHIFT) | ((uint64_t)test_partid << MPAMn_ELx_PARTID_D_SHIFT)); - val_mpam_reg_write(MPAM2_EL2, mpam2_el2); - if (cpor_config_data[cfg_index].config_enable) { + if (enabled_scenarios >= CPOR_SCENARIO_MAX) { + val_print(ERROR, "\n Enabled CPOR scenarios exceed allocated counter rows"); + test_fail++; + goto scenarios_done; + } + + resource_slot = 0; + /* Configure CPOR settings for nodes supporting CPOR */ for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { val_print(DEBUG, "\n Running for cfg_index = %d", cfg_index); @@ -166,21 +245,114 @@ static void payload(void) start_count = 0; end_count = 0; + + /* Preserve selectors before selecting this resource instance. */ + saved_part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + saved_mon_sel = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + /* Select resource instance if RIS feature implemented */ if (val_mpam_msc_supports_ris(msc_index)) val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); - if (val_mpam_supports_cpor(msc_index)) { - val_mpam_configure_cpor(msc_index, test_partid, - cpor_config_data[cfg_index].partition_percent); + if (!val_mpam_supports_cpor(msc_index) || + !val_mpam_supports_csumon(msc_index) || + (val_mpam_get_csumon_count(msc_index) == 0U) || + (val_mpam_get_max_partid(msc_index) < required_max_partid) || + (val_mpam_msc_supports_partid_nrw(msc_index) && + (val_mpam_get_max_intpartid(msc_index) < max_scenario_index))) { + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + continue; + } + + if (resource_slot >= cpor_nodes) { + val_print(ERROR, "\n Resource count changed during test"); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + test_fail++; + continue; + } + current_slot = resource_slot++; + + narrowing_active = val_mpam_msc_supports_partid_nrw(msc_index); + if (narrowing_active) + saved_int_partid = val_mpam_get_partid_mapping(msc_index, test_partid); + + enable_state_saved = val_mpam_msc_supports_partid_endis(msc_index); + saved_enable = enable_state_saved ? + val_mpam_msc_partid_is_enabled(msc_index, test_partid) : true; + + /* Prepare a distinct internal control when narrowing is implemented. */ + if (val_mpam_prepare_partid(msc_index, test_partid, + (uint16_t)cfg_index) != ACS_STATUS_PASS) { + test_fail++; + if (narrowing_active) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_int_partid); + restore_status = 0; + if (enable_state_saved) + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + if (restore_status) + test_fail++; + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + continue; + } + + /* Preserve controls for both narrowed and non-narrowed operation. */ + if (!val_mpam_save_partition_state(msc_index, test_partid, &saved_partition)) { + test_fail++; + if (narrowing_active) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_int_partid); + restore_status = 0; + if (enable_state_saved) + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + if (restore_status) + test_fail++; + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + continue; } + val_mpam_configure_cpor(msc_index, test_partid, + cpor_config_data[cfg_index].partition_percent); if (val_mpam_supports_ccap(msc_index)) val_mpam_configure_ccap(msc_index, test_partid, 0, 100); - buf_size = cache_maxsize * cpor_config_data[cfg_index].cache_percent / 100 / 2; + /* Use the same two-LLC working set to create sustained cache pressure. */ + buf_size = cache_maxsize; page_size = val_memory_page_size(); - num_pages = (uint32_t)((buf_size + page_size - 1) / page_size); + if (page_size == 0U) { + val_print(ERROR, "\n Invalid memory page size"); + test_fail++; + val_mpam_restore_partition_state(msc_index, &saved_partition); + if (narrowing_active) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_int_partid); + if (enable_state_saved) + val_mpam_msc_endis_partid(msc_index, saved_enable, 0, test_partid); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + goto scenarios_done; + } + + num_pages64 = (buf_size / page_size) + ((buf_size % page_size) != 0U); + /* Validate the allocator page count before calculating its byte capacity. */ + if ((num_pages64 == 0U) || (num_pages64 > ACS_UINT32_MAX) || + (num_pages64 > (~(uint64_t)0 / page_size)) || + ((num_pages64 * page_size) < buf_size)) { + val_print(ERROR, "\n Invalid page count for workload buffers"); + test_fail++; + val_mpam_restore_partition_state(msc_index, &saved_partition); + if (narrowing_active) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_int_partid); + if (enable_state_saved) + val_mpam_msc_endis_partid(msc_index, saved_enable, 0, test_partid); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + goto scenarios_done; + } + num_pages = (uint32_t)num_pages64; /*Allocate memory for source and destination buffers */ src_buf = (void *)val_memory_alloc_pages(num_pages); @@ -189,14 +361,36 @@ static void payload(void) if ((src_buf == NULL) || (dest_buf == NULL)) { val_print(ERROR, "\n Mem allocation failed"); val_set_status(index, RESULT_FAIL(01)); + /* Cleanup must run with the original PE labels. */ + val_mpam_reg_write(MPAM2_EL2, mpam2_el2_temp); if (dest_buf != NULL) val_memory_free_pages(dest_buf, num_pages); if (src_buf != NULL) val_memory_free_pages(src_buf, num_pages); + /* Restore controls, mapping, enable state, and selectors. */ + val_mpam_restore_partition_state(msc_index, &saved_partition); + if (narrowing_active) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_int_partid); + if (enable_state_saved) + val_mpam_msc_endis_partid(msc_index, saved_enable, 0, test_partid); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + free_counter_buffer(counter, CPOR_SCENARIO_MAX); return; } + /* val_memcpy() reads from dest_buf. Initialize it and make both + * buffers cold so each CPOR scenario starts with the same workload. + */ + val_memory_set(dest_buf, (uint32_t)buf_size, 0xA5U); + val_pe_cache_invalidate_range((uint64_t)src_buf, buf_size); + val_pe_cache_invalidate_range((uint64_t)dest_buf, buf_size); + val_mem_issue_dsb(); + + /* Preserve monitor 0 before changing its selector, filter, or control. */ + val_mpam_save_csumon_state(msc_index, 0, &saved_csumon); + /* Configure CSU Monitor */ val_mpam_configure_csu_mon(msc_index, test_partid, DEFAULT_PMG, 0); @@ -212,8 +406,12 @@ static void payload(void) start_count = val_mpam_read_csumon(msc_index); val_print(DEBUG, "\n Start Count = 0x%lx", start_count); - /* Start mem copy */ + /* Attribute only the measured workload to the test PARTID. */ + val_mpam_reg_write(MPAM2_EL2, mpam2_el2); val_memcpy(src_buf, dest_buf, buf_size); + val_mem_issue_dsb(); + val_mpam_reg_write(MPAM2_EL2, mpam2_el2_temp); + /* Wait for some time before the memcpy settles and counters update */ val_time_delay_ms(TIMEOUT_MEDIUM); @@ -225,43 +423,60 @@ static void payload(void) val_print(ERROR, "\n CSU monitor did not advance for msc_index : %d", msc_index); test_fail++; + counter[enabled_scenarios][current_slot] = 0; + } else { + /* Compare only the cache usage added by this workload. */ + counter[enabled_scenarios][current_slot] = end_count - start_count; } /* Disable CSU MON */ val_mpam_csumon_disable(msc_index); - /* Read CSU MON */ - counter[enabled_scenarios][msc_index] = end_count; + /* Restore the monitor and partition controls modified by this scenario. */ + val_mpam_restore_csumon_state(msc_index, &saved_csumon); + val_mpam_restore_partition_state(msc_index, &saved_partition); /* Free the buffers to the heap manager */ - if (dest_buf != NULL) { - val_pe_cache_invalidate_range((uint64_t)dest_buf, buf_size); - val_mem_issue_dsb(); - val_memory_free_pages(dest_buf, num_pages); - } - if (src_buf != NULL) { - val_pe_cache_invalidate_range((uint64_t)src_buf, buf_size); - val_mem_issue_dsb(); - val_memory_free_pages(src_buf, num_pages); - } + val_pe_cache_invalidate_range((uint64_t)dest_buf, buf_size); + val_mem_issue_dsb(); + val_memory_free_pages(dest_buf, num_pages); + val_pe_cache_invalidate_range((uint64_t)src_buf, buf_size); + val_mem_issue_dsb(); + val_memory_free_pages(src_buf, num_pages); + + /* Restore the original mapping, enable state, and selectors. */ + if (narrowing_active) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_int_partid); + restore_status = 0; + if (enable_state_saved) + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + if (restore_status) + test_fail++; + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); } } enabled_scenarios++; } } +scenarios_done: /* Restore MPAM2_EL2 settings */ val_mpam_reg_write(MPAM2_EL2, mpam2_el2_temp); - /* Compare the stream copy latencies for all the scenarios */ - for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { + /* Compare each resource instance independently across scenarios. */ + for (resource_slot = 0; resource_slot < cpor_nodes; resource_slot++) { for (cfg_index = 1; cfg_index < enabled_scenarios; cfg_index++) { - if (counter[cfg_index][msc_index] > counter[cfg_index-1][msc_index]) { - val_print(ERROR, "\n Failed for msc_index : %d", msc_index); + if (counter[cfg_index][resource_slot] > + counter[cfg_index - 1][resource_slot]) { + val_print(ERROR, "\n Failed for resource slot : %d", resource_slot); val_print(ERROR, "\n cfg_index : %d", cfg_index-1); - val_print(ERROR, ", CSU Mon Count :0x%lx", counter[cfg_index-1][msc_index]); + val_print(ERROR, ", CSU Mon Count :0x%lx", + counter[cfg_index - 1][resource_slot]); val_print(ERROR, "\n cfg_index : %d", cfg_index); - val_print(ERROR, ", CSU Mon Count :0x%lx", counter[cfg_index][msc_index]); + val_print(ERROR, ", CSU Mon Count :0x%lx", + counter[cfg_index][resource_slot]); test_fail++; } } @@ -272,6 +487,7 @@ static void payload(void) else val_set_status(index, RESULT_PASS); + free_counter_buffer(counter, CPOR_SCENARIO_MAX); return; } diff --git a/test_pool/mpam/partition002.c b/test_pool/mpam/partition002.c index bde1abc6..790d1cf2 100644 --- a/test_pool/mpam/partition002.c +++ b/test_pool/mpam/partition002.c @@ -42,6 +42,20 @@ static ccap_config_t ccap_config_data[] = { {"CCAP check for 25% partition size", 25, 75, true} }; +static void free_counter_buffer(uint64_t **counter, uint32_t row_count) +{ + uint32_t row; + + if (counter == NULL) + return; + + for (row = 0; row < row_count; row++) { + if (counter[row] != NULL) + val_memory_free(counter[row]); + } + val_memory_free(counter); +} + static void payload(void) { uint8_t enabled_scenarios = 0; @@ -54,6 +68,7 @@ static void payload(void) uint64_t buf_size; uint32_t page_size; uint32_t num_pages; + uint64_t num_pages64; uint64_t nrdy_timeout; uint64_t **counter; uint64_t it; @@ -64,11 +79,23 @@ static void payload(void) uint32_t rsrc_index; uint32_t llc_index; uint64_t cache_identifier; - uint32_t cache_maxsize; + uint32_t cache_maxsize = 0; uint32_t test_partid = 3; //Selecting distinct partid uint32_t csumon_count = 0; uint32_t index = val_pe_get_index_mpid(val_pe_get_mpid()); uint32_t test_fail = 0; + uint32_t test_run = 0; + uint32_t saved_part_sel = 0; + uint32_t saved_mon_sel = 0; + uint16_t saved_mapping = 0; + uint32_t mapping_saved; + uint32_t restore_status; + bool saved_enable; + bool enable_state_saved; + MPAM_CSUMON_STATE saved_csumon; + MPAM_PARTITION_STATE saved_partition; + uint32_t resource_slot; + uint32_t current_slot; /* Get the Index for LLC */ llc_index = val_cache_get_llc_index(); @@ -89,6 +116,9 @@ static void payload(void) /* Get MPAM related information for LLC */ for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { + saved_part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + saved_mon_sel = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + rsrc_node_cnt = val_mpam_get_info(MPAM_MSC_RSRC_COUNT, msc_index, 0); for (rsrc_index = 0; rsrc_index < rsrc_node_cnt; rsrc_index++) { @@ -98,6 +128,9 @@ static void payload(void) if (val_mpam_get_info(MPAM_MSC_RSRC_DESC1, msc_index, rsrc_index) != cache_identifier) continue; + if (val_mpam_msc_supports_ris(msc_index)) + val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); + if (val_mpam_supports_ccap(msc_index)) { cache_maxsize = GET_MAX_VALUE(cache_maxsize, val_cache_get_info(CACHE_SIZE, llc_index)); @@ -109,6 +142,8 @@ static void payload(void) } test_partid = GET_MIN_VALUE(test_partid, val_mpam_get_max_partid(msc_index)); } + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); } val_print(DEBUG, "\n CCAP Nodes = %d", ccap_nodes); @@ -126,8 +161,19 @@ static void payload(void) * uint64_t counter[CCAP_SCENARIO_MAX][ccap_nodes] */ counter = (uint64_t **) val_memory_alloc(CCAP_SCENARIO_MAX * sizeof (uint64_t *)); + if (counter == NULL) { + val_set_status(index, RESULT_FAIL(01)); + return; + } + val_memory_set(counter, CCAP_SCENARIO_MAX * sizeof(uint64_t *), 0); for (it = 0; it < CCAP_SCENARIO_MAX; it++) { - counter[it] = val_memory_alloc(msc_node_cnt * sizeof (uint64_t)); + counter[it] = val_memory_alloc(ccap_nodes * sizeof(uint64_t)); + if (counter[it] == NULL) { + free_counter_buffer(counter, CCAP_SCENARIO_MAX); + val_set_status(index, RESULT_FAIL(01)); + return; + } + val_memory_set(counter[it], ccap_nodes * sizeof(uint64_t), 0); } mpam2_el2 = val_mpam_reg_read(MPAM2_EL2); @@ -136,7 +182,14 @@ static void payload(void) for (cfg_index = 0; cfg_index < sizeof(ccap_config_data)/sizeof(ccap_config_t); cfg_index++) { - test_partid = test_partid + cfg_index; + if (ccap_config_data[cfg_index].config_enable && + (enabled_scenarios >= CCAP_SCENARIO_MAX)) { + val_print(ERROR, "\n Enabled scenario count exceeds counter capacity"); + test_fail++; + break; + } + + /* Scenarios are sequential, so reuse one request PARTID within its valid range. */ /* Clear the PARTID_D & PMG_D bits in mpam2_el2 before writing to them */ mpam2_el2 = CLEAR_BITS_M_TO_N(mpam2_el2, MPAMn_ELx_PARTID_D_SHIFT+15, MPAMn_ELx_PARTID_D_SHIFT); @@ -147,9 +200,8 @@ static void payload(void) mpam2_el2 |= (((uint64_t)DEFAULT_PMG << MPAMn_ELx_PMG_D_SHIFT) | ((uint64_t)test_partid << MPAMn_ELx_PARTID_D_SHIFT)); - val_mpam_reg_write(MPAM2_EL2, mpam2_el2); - if (ccap_config_data[cfg_index].config_enable) { + resource_slot = 0; /* Configure CCAP settings for nodes supporting CCAP */ for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { val_print(DEBUG, "\n Running for cfg_index = %d", cfg_index); @@ -167,24 +219,31 @@ static void payload(void) start_count = 0; end_count = 0; - /* Select resource instance if RIS feature implemented */ - if (val_mpam_msc_supports_ris(msc_index)) - val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); - - if (val_mpam_supports_ccap(msc_index)) { - val_mpam_configure_ccap(msc_index, test_partid, - SOFTLIMIT_DIS, - ccap_config_data[cfg_index].partition_percent); + /* Calculate the workload and page count without narrowing intermediate values. */ + buf_size = ((uint64_t)cache_maxsize * + ccap_config_data[cfg_index].cache_percent) / 100U / 2U; + page_size = val_memory_page_size(); + if (page_size == 0U) { + val_print(ERROR, "\n Invalid memory page size"); + val_set_status(index, RESULT_FAIL(01)); + free_counter_buffer(counter, CCAP_SCENARIO_MAX); + return; } - if (val_mpam_supports_cpor(msc_index)) - val_mpam_configure_cpor(msc_index, test_partid, 100); - - buf_size = cache_maxsize * ccap_config_data[cfg_index].cache_percent / 100 / 2; - page_size = val_memory_page_size(); - num_pages = (uint32_t)((buf_size + page_size - 1) / page_size); + num_pages64 = (buf_size / page_size) + ((buf_size % page_size) != 0U); + if ((num_pages64 == 0U) || (num_pages64 > ACS_UINT32_MAX) || + (num_pages64 > (~(uint64_t)0 / page_size)) || + ((num_pages64 * page_size) < buf_size)) { + val_print(ERROR, "\n Invalid page count for workload buffers"); + val_set_status(index, RESULT_FAIL(01)); + free_counter_buffer(counter, CCAP_SCENARIO_MAX); + return; + } + num_pages = (uint32_t)num_pages64; - /*Allocate memory for source and destination buffers */ + /* Start each resource iteration without stale allocation pointers. */ + src_buf = NULL; + dest_buf = NULL; src_buf = (void *)val_memory_alloc_pages(num_pages); dest_buf = (void *)val_memory_alloc_pages(num_pages); @@ -195,9 +254,98 @@ static void payload(void) val_memory_free_pages(dest_buf, num_pages); if (src_buf != NULL) val_memory_free_pages(src_buf, num_pages); + src_buf = NULL; + dest_buf = NULL; + free_counter_buffer(counter, CCAP_SCENARIO_MAX); return; } + /* Preserve both selectors before selecting this resource instance. */ + saved_part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + saved_mon_sel = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + + /* Select and validate this resource only after fallible allocation succeeds. */ + if (val_mpam_msc_supports_ris(msc_index)) + val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); + + if (!val_mpam_supports_ccap(msc_index) || + !val_mpam_supports_csumon(msc_index) || + (val_mpam_get_csumon_count(msc_index) == 0U)) { + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + continue; + } + + if (resource_slot >= ccap_nodes) { + val_print(ERROR, "\n Resource count changed during test"); + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + test_fail++; + continue; + } + current_slot = resource_slot++; + + mapping_saved = 0; + if (val_mpam_msc_supports_partid_nrw(msc_index)) { + saved_mapping = val_mpam_get_partid_mapping(msc_index, test_partid); + mapping_saved = 1; + } + + /* Save the request PARTID enable state before preparation. */ + enable_state_saved = val_mpam_msc_supports_partid_endis(msc_index); + saved_enable = enable_state_saved ? + val_mpam_msc_partid_is_enabled(msc_index, test_partid) : true; + + /* Prepare the request PARTID for narrowed or non-narrowed controls. */ + if (val_mpam_prepare_partid(msc_index, test_partid, 0) != ACS_STATUS_PASS) { + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + restore_status = 0; + if (enable_state_saved) + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + if (restore_status) + test_fail++; + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + test_fail++; + continue; + } + + /* Preserve this PARTID's controls before applying the scenario. */ + if (!val_mpam_save_partition_state(msc_index, test_partid, &saved_partition)) { + test_fail++; + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + if (enable_state_saved && + val_mpam_msc_endis_partid(msc_index, saved_enable, 0, test_partid)) + test_fail++; + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + continue; + } + test_run = 1; + + if (val_mpam_supports_ccap(msc_index)) { + val_mpam_configure_ccap(msc_index, test_partid, + SOFTLIMIT_DIS, + ccap_config_data[cfg_index].partition_percent); + } + + if (val_mpam_supports_cpor(msc_index)) + val_mpam_configure_cpor(msc_index, test_partid, 100); + + /* Preserve monitor 0 before changing its filter, control, or VALUE. */ + val_mpam_save_csumon_state(msc_index, 0, &saved_csumon); + /* Configure CSU Monitor */ val_mpam_configure_csu_mon(msc_index, test_partid, DEFAULT_PMG, 0); @@ -213,8 +361,12 @@ static void payload(void) start_count = val_mpam_read_csumon(msc_index); val_print(DEBUG, "\n Start Count = 0x%lx", start_count); - /* Start mem copy */ + /* Apply the prepared PARTID only to the measured workload. */ + val_mpam_reg_write(MPAM2_EL2, mpam2_el2); val_memcpy(src_buf, dest_buf, buf_size); + val_mem_issue_dsb(); + val_mpam_reg_write(MPAM2_EL2, mpam2_el2_temp); + /* Wait for some time before the memcpy settles and counters update */ val_time_delay_ms(TIMEOUT_MEDIUM); @@ -232,18 +384,34 @@ static void payload(void) val_mpam_csumon_disable(msc_index); /* Read CSU MON */ - counter[enabled_scenarios][msc_index] = end_count; + counter[enabled_scenarios][current_slot] = end_count; + + val_mpam_restore_csumon_state(msc_index, &saved_csumon); + val_mpam_restore_partition_state(msc_index, &saved_partition); + + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + restore_status = 0; + if (enable_state_saved) + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + if (restore_status) + test_fail++; + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); /* Free the buffers to the heap manager */ if (dest_buf != NULL) { val_pe_cache_invalidate_range((uint64_t)dest_buf, buf_size); val_mem_issue_dsb(); val_memory_free_pages(dest_buf, num_pages); + dest_buf = NULL; } if (src_buf != NULL) { val_pe_cache_invalidate_range((uint64_t)src_buf, buf_size); val_mem_issue_dsb(); val_memory_free_pages(src_buf, num_pages); + src_buf = NULL; } } } @@ -254,15 +422,24 @@ static void payload(void) /* Restore MPAM2_EL2 settings */ val_mpam_reg_write(MPAM2_EL2, mpam2_el2_temp); - /* Compare the stream copy latencies for all the scenarios */ - for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { + /* Compare each resource instance independently across scenarios. */ + for (resource_slot = 0; resource_slot < ccap_nodes; resource_slot++) { for (cfg_index = 1; cfg_index < enabled_scenarios; cfg_index++) { - if (counter[cfg_index][msc_index] > counter[cfg_index-1][msc_index]) { - val_print(ERROR, "\n Failed for msc_index : %d", msc_index); - val_print(ERROR, "\n cfg_index : %d", cfg_index-1); - val_print(ERROR, ", CSU Mon Count :0x%lx", counter[cfg_index-1][msc_index]); + if ((counter[cfg_index][resource_slot] == 0U) && + (counter[cfg_index - 1][resource_slot] == 0U)) + continue; + + if ((counter[cfg_index][resource_slot] == 0U) || + (counter[cfg_index - 1][resource_slot] == 0U) || + (counter[cfg_index][resource_slot] > + counter[cfg_index - 1][resource_slot])) { + val_print(ERROR, "\n Failed for resource slot : %d", resource_slot); + val_print(ERROR, "\n cfg_index : %d", cfg_index - 1); + val_print(ERROR, ", CSU Mon Count :0x%lx", + counter[cfg_index - 1][resource_slot]); val_print(ERROR, "\n cfg_index : %d", cfg_index); - val_print(ERROR, ", CSU Mon Count :0x%lx", counter[cfg_index][msc_index]); + val_print(ERROR, ", CSU Mon Count :0x%lx", + counter[cfg_index][resource_slot]); test_fail++; } } @@ -270,9 +447,12 @@ static void payload(void) if (test_fail) val_set_status(index, RESULT_FAIL(02)); + else if (!test_run) + val_set_status(index, RESULT_SKIP(04)); else val_set_status(index, RESULT_PASS); + free_counter_buffer(counter, CCAP_SCENARIO_MAX); return; } diff --git a/test_pool/mpam/partition003.c b/test_pool/mpam/partition003.c index 3eef0fad..2b9adcd5 100644 --- a/test_pool/mpam/partition003.c +++ b/test_pool/mpam/partition003.c @@ -43,6 +43,20 @@ static payload_config_t test_config_data[] = { {"Check for 75% CCAP & 25% CPOR partition size", 75, 25, 75, true} }; +static void free_counter_buffer(uint64_t **counter, uint32_t row_count) +{ + uint32_t row; + + if (counter == NULL) + return; + + for (row = 0; row < row_count; row++) { + if (counter[row] != NULL) + val_memory_free(counter[row]); + } + val_memory_free(counter); +} + static void payload(void) { uint8_t enabled_scenarios = 0; @@ -55,6 +69,7 @@ static void payload(void) uint64_t buf_size; uint32_t page_size; uint32_t num_pages; + uint64_t num_pages64; uint64_t nrdy_timeout; uint64_t **counter; uint64_t it; @@ -65,11 +80,24 @@ static void payload(void) uint32_t rsrc_index; uint32_t llc_index; uint64_t cache_identifier; - uint32_t cache_maxsize; + uint32_t cache_maxsize = 0; uint32_t test_partid = 5; //Selecting distinct partid uint32_t csumon_count = 0; uint32_t index = val_pe_get_index_mpid(val_pe_get_mpid()); uint32_t test_fail = 0; + uint32_t test_run = 0; + uint32_t saved_part_sel = 0; + uint32_t saved_mon_sel = 0; + uint16_t saved_mapping = 0; + uint32_t mapping_saved; + uint32_t restore_status; + bool saved_enable; + bool enable_state_saved; + MPAM_CSUMON_STATE saved_csumon; + MPAM_PARTITION_STATE saved_partition; + uint32_t resource_slot; + uint32_t current_slot; + uint32_t counter_row_size; /* Get the Index for LLC */ llc_index = val_cache_get_llc_index(); @@ -90,6 +118,9 @@ static void payload(void) /* Get MPAM related information for LLC */ for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { + saved_part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + saved_mon_sel = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + rsrc_node_cnt = val_mpam_get_info(MPAM_MSC_RSRC_COUNT, msc_index, 0); for (rsrc_index = 0; rsrc_index < rsrc_node_cnt; rsrc_index++) { @@ -99,6 +130,9 @@ static void payload(void) if (val_mpam_get_info(MPAM_MSC_RSRC_DESC1, msc_index, rsrc_index) != cache_identifier) continue; + if (val_mpam_msc_supports_ris(msc_index)) + val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); + if (val_mpam_supports_ccap(msc_index) && val_mpam_supports_cpor(msc_index)) { cache_maxsize = GET_MAX_VALUE(cache_maxsize, val_cache_get_info(CACHE_SIZE, llc_index)); @@ -110,6 +144,8 @@ static void payload(void) } test_partid = GET_MIN_VALUE(test_partid, val_mpam_get_max_partid(msc_index)); } + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); } val_print(DEBUG, "\n CCAP CPOR Nodes = %d", ccap_cpor_nodes); @@ -123,12 +159,31 @@ static void payload(void) return; } + /* Validate the row size before passing it to the 32-bit allocator API. */ + if (ccap_cpor_nodes > (ACS_UINT32_MAX / sizeof(uint64_t))) { + val_print(ERROR, "\n Counter row allocation size exceeds supported range"); + val_set_status(index, RESULT_FAIL(01)); + return; + } + counter_row_size = ccap_cpor_nodes * sizeof(uint64_t); + /* Dynamically create the CSU Mon Counter buffer * uint64_t counter[SCENARIO_MAX][ccap_cpor_nodes] */ counter = (uint64_t **) val_memory_alloc(SCENARIO_MAX * sizeof (uint64_t *)); + if (counter == NULL) { + val_set_status(index, RESULT_FAIL(01)); + return; + } + val_memory_set(counter, SCENARIO_MAX * sizeof(uint64_t *), 0); for (it = 0; it < SCENARIO_MAX; it++) { - counter[it] = val_memory_alloc(msc_node_cnt * sizeof (uint64_t)); + counter[it] = val_memory_alloc(counter_row_size); + if (counter[it] == NULL) { + free_counter_buffer(counter, SCENARIO_MAX); + val_set_status(index, RESULT_FAIL(01)); + return; + } + val_memory_set(counter[it], counter_row_size, 0); } mpam2_el2 = val_mpam_reg_read(MPAM2_EL2); @@ -137,7 +192,7 @@ static void payload(void) for (cfg_index = 0; cfg_index < sizeof(test_config_data)/sizeof(payload_config_t); cfg_index++) { - test_partid = test_partid + cfg_index; + /* Scenarios are sequential, so reuse one request PARTID within its valid range. */ /* Clear the PARTID_D & PMG_D bits in mpam2_el2 before writing to them */ mpam2_el2 = CLEAR_BITS_M_TO_N(mpam2_el2, MPAMn_ELx_PARTID_D_SHIFT+15, MPAMn_ELx_PARTID_D_SHIFT); @@ -148,8 +203,14 @@ static void payload(void) mpam2_el2 |= (((uint64_t)DEFAULT_PMG << MPAMn_ELx_PMG_D_SHIFT) | ((uint64_t)test_partid << MPAMn_ELx_PARTID_D_SHIFT)); - val_mpam_reg_write(MPAM2_EL2, mpam2_el2); if (test_config_data[cfg_index].config_enable) { + resource_slot = 0; + if (enabled_scenarios >= SCENARIO_MAX) { + val_print(ERROR, "\n Enabled scenarios exceed allocated counter rows"); + test_fail++; + break; + } + /* Configure settings for nodes supporting */ for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { val_print(DEBUG, "\n Running for cfg_index = %d", cfg_index); @@ -167,10 +228,122 @@ static void payload(void) start_count = 0; end_count = 0; - /* Select resource instance if RIS feature implemented */ + /* Calculate the workload and page count without narrowing intermediate values. */ + buf_size = ((uint64_t)cache_maxsize * + test_config_data[cfg_index].cache_percent) / 100U / 2U; + page_size = val_memory_page_size(); + if (page_size == 0U) { + val_print(ERROR, "\n Invalid memory page size"); + val_set_status(index, RESULT_FAIL(01)); + free_counter_buffer(counter, SCENARIO_MAX); + return; + } + + num_pages64 = (buf_size / page_size) + ((buf_size % page_size) != 0U); + if ((num_pages64 == 0U) || (num_pages64 > ACS_UINT32_MAX) || + (num_pages64 > (~(uint64_t)0 / page_size)) || + ((num_pages64 * page_size) < buf_size)) { + val_print(ERROR, "\n Invalid page count for workload buffers"); + val_set_status(index, RESULT_FAIL(01)); + free_counter_buffer(counter, SCENARIO_MAX); + return; + } + num_pages = (uint32_t)num_pages64; + + /* Start each resource iteration without stale allocation pointers. */ + src_buf = NULL; + dest_buf = NULL; + src_buf = (void *)val_memory_alloc_pages(num_pages); + dest_buf = (void *)val_memory_alloc_pages(num_pages); + + if ((src_buf == NULL) || (dest_buf == NULL)) { + val_print(ERROR, "\n Mem allocation failed"); + val_set_status(index, RESULT_FAIL(01)); + if (dest_buf != NULL) + val_memory_free_pages(dest_buf, num_pages); + if (src_buf != NULL) + val_memory_free_pages(src_buf, num_pages); + src_buf = NULL; + dest_buf = NULL; + free_counter_buffer(counter, SCENARIO_MAX); + return; + } + + /* Preserve both selectors before selecting this resource instance. */ + saved_part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + saved_mon_sel = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + + /* Select and validate this resource only after fallible allocation succeeds. */ if (val_mpam_msc_supports_ris(msc_index)) val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); + if (!val_mpam_supports_ccap(msc_index) || + !val_mpam_supports_cpor(msc_index) || + !val_mpam_supports_csumon(msc_index) || + (val_mpam_get_csumon_count(msc_index) == 0U)) { + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + continue; + } + + if (resource_slot >= ccap_cpor_nodes) { + val_print(ERROR, "\n Resource count changed during test"); + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + test_fail++; + continue; + } + current_slot = resource_slot++; + + mapping_saved = 0; + if (val_mpam_msc_supports_partid_nrw(msc_index)) { + saved_mapping = val_mpam_get_partid_mapping(msc_index, test_partid); + mapping_saved = 1; + } + + /* Save the request PARTID enable state before preparation. */ + enable_state_saved = val_mpam_msc_supports_partid_endis(msc_index); + saved_enable = enable_state_saved ? + val_mpam_msc_partid_is_enabled(msc_index, test_partid) : true; + + /* Prepare the request PARTID for narrowed or non-narrowed controls. */ + if (val_mpam_prepare_partid(msc_index, test_partid, 0) != ACS_STATUS_PASS) { + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + restore_status = 0; + if (enable_state_saved) + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + if (restore_status) + test_fail++; + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + test_fail++; + continue; + } + + /* Preserve this PARTID's controls before applying the scenario. */ + if (!val_mpam_save_partition_state(msc_index, test_partid, &saved_partition)) { + test_fail++; + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + if (enable_state_saved && + val_mpam_msc_endis_partid(msc_index, saved_enable, 0, test_partid)) + test_fail++; + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + continue; + } + test_run = 1; + if (val_mpam_supports_ccap(msc_index) && val_mpam_supports_cpor(msc_index)) { /* Configure CCAP Partitoning */ val_mpam_configure_ccap(msc_index, test_partid, SOFTLIMIT_DIS, @@ -185,23 +358,8 @@ static void payload(void) val_mpam_configure_cpor(msc_index, test_partid, 100); } - buf_size = cache_maxsize * test_config_data[cfg_index].cache_percent / 100 / 2; - page_size = val_memory_page_size(); - num_pages = (uint32_t)((buf_size + page_size - 1) / page_size); - - /*Allocate memory for source and destination buffers */ - src_buf = (void *)val_memory_alloc_pages(num_pages); - dest_buf = (void *)val_memory_alloc_pages(num_pages); - - if ((src_buf == NULL) || (dest_buf == NULL)) { - val_print(ERROR, "\n Mem allocation failed"); - val_set_status(index, RESULT_FAIL(01)); - if (dest_buf != NULL) - val_memory_free_pages(dest_buf, num_pages); - if (src_buf != NULL) - val_memory_free_pages(src_buf, num_pages); - return; - } + /* Preserve monitor 0 before changing its filter, control, or VALUE. */ + val_mpam_save_csumon_state(msc_index, 0, &saved_csumon); /* Configure CSU Monitor */ val_mpam_configure_csu_mon(msc_index, test_partid, DEFAULT_PMG, 0); @@ -218,8 +376,12 @@ static void payload(void) start_count = val_mpam_read_csumon(msc_index); val_print(DEBUG, "\n Start Count = 0x%lx", start_count); - /* Start mem copy */ + /* Apply the prepared PARTID only to the measured workload. */ + val_mpam_reg_write(MPAM2_EL2, mpam2_el2); val_memcpy(src_buf, dest_buf, buf_size); + val_mem_issue_dsb(); + val_mpam_reg_write(MPAM2_EL2, mpam2_el2_temp); + /* Wait for some time before the memcpy settles and counters update */ val_time_delay_ms(TIMEOUT_MEDIUM); @@ -237,18 +399,34 @@ static void payload(void) val_mpam_csumon_disable(msc_index); /* Read CSU MON */ - counter[enabled_scenarios][msc_index] = end_count; + counter[enabled_scenarios][current_slot] = end_count; + + val_mpam_restore_csumon_state(msc_index, &saved_csumon); + val_mpam_restore_partition_state(msc_index, &saved_partition); + + if (mapping_saved) + val_mpam_set_partid_mapping(msc_index, test_partid, saved_mapping); + restore_status = 0; + if (enable_state_saved) + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable, + 0, test_partid); + if (restore_status) + test_fail++; + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); /* Free the buffers to the heap manager */ if (dest_buf != NULL) { val_pe_cache_invalidate_range((uint64_t)dest_buf, buf_size); val_mem_issue_dsb(); val_memory_free_pages(dest_buf, num_pages); + dest_buf = NULL; } if (src_buf != NULL) { val_pe_cache_invalidate_range((uint64_t)src_buf, buf_size); val_mem_issue_dsb(); val_memory_free_pages(src_buf, num_pages); + src_buf = NULL; } } } @@ -259,15 +437,24 @@ static void payload(void) /* Restore MPAM2_EL2 settings */ val_mpam_reg_write(MPAM2_EL2, mpam2_el2_temp); - /* Compare the stream copy latencies for all the scenarios */ - for (msc_index = 0; msc_index < msc_node_cnt; msc_index++) { + /* Compare each resource instance independently across scenarios. */ + for (resource_slot = 0; resource_slot < ccap_cpor_nodes; resource_slot++) { for (cfg_index = 1; cfg_index < enabled_scenarios; cfg_index++) { - if (counter[cfg_index][msc_index] > counter[cfg_index-1][msc_index]) { - val_print(ERROR, "\n Failed for msc_index : %d", msc_index); - val_print(ERROR, "\n cfg_index : %d", cfg_index-1); - val_print(ERROR, ", CSU Mon Count :0x%lx", counter[cfg_index-1][msc_index]); + if ((counter[cfg_index][resource_slot] == 0U) && + (counter[cfg_index - 1][resource_slot] == 0U)) + continue; + + if ((counter[cfg_index][resource_slot] == 0U) || + (counter[cfg_index - 1][resource_slot] == 0U) || + (counter[cfg_index][resource_slot] > + counter[cfg_index - 1][resource_slot])) { + val_print(ERROR, "\n Failed for resource slot : %d", resource_slot); + val_print(ERROR, "\n cfg_index : %d", cfg_index - 1); + val_print(ERROR, ", CSU Mon Count :0x%lx", + counter[cfg_index - 1][resource_slot]); val_print(ERROR, "\n cfg_index : %d", cfg_index); - val_print(ERROR, ", CSU Mon Count :0x%lx", counter[cfg_index][msc_index]); + val_print(ERROR, ", CSU Mon Count :0x%lx", + counter[cfg_index][resource_slot]); test_fail++; } } @@ -275,9 +462,12 @@ static void payload(void) if (test_fail) val_set_status(index, RESULT_FAIL(02)); + else if (!test_run) + val_set_status(index, RESULT_SKIP(04)); else val_set_status(index, RESULT_PASS); + free_counter_buffer(counter, SCENARIO_MAX); return; } diff --git a/test_pool/mpam/partition004.c b/test_pool/mpam/partition004.c index 4a20444d..5170381a 100644 --- a/test_pool/mpam/partition004.c +++ b/test_pool/mpam/partition004.c @@ -76,6 +76,24 @@ payload(void) uint32_t num_mon = 0; uint32_t page_size; uint32_t num_pages; + uint64_t num_pages64; + uint32_t saved_part_sel = 0; + uint32_t saved_mon_sel = 0; + uint16_t saved_map_x = 0; + uint16_t saved_map_y = 0; + uint16_t saved_map_z = 0; + uint32_t mappings_saved; + uint32_t restore_status; + bool saved_enable_x; + bool saved_enable_y; + bool saved_enable_z; + bool enable_state_saved; + MPAM_CSUMON_STATE saved_csumon; + bool csumon_state_saved; + MPAM_PARTITION_STATE saved_partition_x; + MPAM_PARTITION_STATE saved_partition_y; + MPAM_PARTITION_STATE saved_partition_z; + bool partition_state_saved; /* Check if LLC is valid */ if (llc_idx == CACHE_TABLE_EMPTY) { @@ -139,15 +157,21 @@ payload(void) num_mon = 0; start_count = 0; end_count = 0; + mappings_saved = 0; - /* Select resource instance if RIS feature implemented */ - if (val_mpam_msc_supports_ris(msc_index)) + /* Preserve both selectors before selecting a resource instance. */ + saved_part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + saved_mon_sel = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + if (val_mpam_msc_supports_ris(msc_index)) { val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); + } /* Check if MSC supports CASSOC */ if (!val_mpam_supports_cassoc(msc_index)) { val_print(DEBUG, "\n MSC %d does not support CASSOC partitioning, skipping", msc_index); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); continue; } @@ -158,19 +182,34 @@ payload(void) if (num_mon == 0) { val_print(DEBUG, "\n MSC %d does not have CSU monitors, skipping MSC", msc_index); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); continue; } - /* Disable CCAP and CPOR settings -> Set them to max */ - if (val_mpam_supports_cpor(msc_index)) - val_mpam_configure_cpor(msc_index, test_partid, 100); - - if (val_mpam_supports_ccap(msc_index)) - val_mpam_configure_ccap(msc_index, test_partid, 0, 100); - /* Allocate memory for source and destination buffers */ page_size = val_memory_page_size(); - num_pages = (uint32_t)((BUFFER_SIZE + page_size - 1) / page_size); + if (page_size == 0U) { + val_print(ERROR, "\n Invalid memory page size"); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + val_set_status(index, RESULT_FAIL(01)); + return; + } + + num_pages64 = (BUFFER_SIZE / page_size) + + ((BUFFER_SIZE % page_size) != 0U); + /* Validate the allocator page count before calculating its byte capacity. */ + if ((num_pages64 == 0U) || (num_pages64 > ACS_UINT32_MAX) || + (num_pages64 > (~(uint64_t)0 / page_size)) || + ((num_pages64 * page_size) < BUFFER_SIZE)) { + val_print(ERROR, "\n Invalid page count for workload buffers"); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + val_set_status(index, RESULT_FAIL(01)); + return; + } + num_pages = (uint32_t)num_pages64; src_buf = (void *)val_memory_alloc_pages(num_pages); dest_buf = (void *)val_memory_alloc_pages(num_pages); @@ -181,15 +220,113 @@ payload(void) val_memory_free_pages(dest_buf, num_pages); if (src_buf != NULL) val_memory_free_pages(src_buf, num_pages); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); return; } + if (val_mpam_msc_supports_partid_nrw(msc_index)) { + if (val_mpam_get_max_intpartid(msc_index) < 2U) { + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + continue; + } + saved_map_x = val_mpam_get_partid_mapping(msc_index, test_partid); + saved_map_y = val_mpam_get_partid_mapping(msc_index, test_partid - 1); + saved_map_z = val_mpam_get_partid_mapping(msc_index, test_partid - 2); + mappings_saved = 1; + } + + /* Save all request PARTID enable states before preparation. */ + enable_state_saved = val_mpam_msc_supports_partid_endis(msc_index); + saved_enable_x = enable_state_saved ? + val_mpam_msc_partid_is_enabled(msc_index, test_partid) : true; + saved_enable_y = enable_state_saved ? + val_mpam_msc_partid_is_enabled(msc_index, test_partid - 1) : true; + saved_enable_z = enable_state_saved ? + val_mpam_msc_partid_is_enabled(msc_index, test_partid - 2) : true; + + csumon_state_saved = false; + partition_state_saved = false; + saved_el2 = val_mpam_reg_read(MPAM2_EL2); + + /* Give each request PARTID a distinct control PARTID when narrowing is supported. */ + if ((val_mpam_prepare_partid(msc_index, test_partid, 0) != ACS_STATUS_PASS) || + (val_mpam_prepare_partid(msc_index, test_partid - 1, 1) != ACS_STATUS_PASS) || + (val_mpam_prepare_partid(msc_index, test_partid - 2, 2) != ACS_STATUS_PASS)) { + test_fail++; + if (mappings_saved) { + val_mpam_set_partid_mapping(msc_index, test_partid, saved_map_x); + val_mpam_set_partid_mapping(msc_index, test_partid - 1, saved_map_y); + val_mpam_set_partid_mapping(msc_index, test_partid - 2, saved_map_z); + } + restore_status = 0; + if (enable_state_saved) { + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable_x, + 0, test_partid); + restore_status |= val_mpam_msc_endis_partid(msc_index, saved_enable_y, + 0, test_partid - 1); + restore_status |= val_mpam_msc_endis_partid(msc_index, saved_enable_z, + 0, test_partid - 2); + } + if (restore_status) + val_print(ERROR, "\n Failed to restore PARTID enable state"); + if (csumon_state_saved) + val_mpam_restore_csumon_state(msc_index, &saved_csumon); + + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + continue; + } + + /* Preserve all effective controls before applying CASSOC scenarios. */ + if (!val_mpam_save_partition_state(msc_index, test_partid, + &saved_partition_x)) { + test_fail++; + goto cleanup; + } + if (!val_mpam_save_partition_state(msc_index, test_partid - 1, + &saved_partition_y)) { + test_fail++; + val_mpam_restore_partition_state(msc_index, &saved_partition_x); + goto cleanup; + } + if (!val_mpam_save_partition_state(msc_index, test_partid - 2, + &saved_partition_z)) { + test_fail++; + val_mpam_restore_partition_state(msc_index, &saved_partition_x); + val_mpam_restore_partition_state(msc_index, &saved_partition_y); + goto cleanup; + } + partition_state_saved = true; + + /* Disable CCAP and CPOR settings -> Set them to max */ + if (val_mpam_supports_cpor(msc_index)) + val_mpam_configure_cpor(msc_index, test_partid, 100); + + if (val_mpam_supports_ccap(msc_index)) + val_mpam_configure_ccap(msc_index, test_partid, 0, 100); + + /* Start the first scenario with the same cold-buffer state as later scenarios. */ + val_memory_set(dest_buf, BUFFER_SIZE, 0xA5U); + val_pe_cache_invalidate_range((uint64_t)src_buf, BUFFER_SIZE); + val_pe_cache_invalidate_range((uint64_t)dest_buf, BUFFER_SIZE); + val_mem_issue_dsb(); + /* Test runs on atleast one MSC */ test_skip = 0; /* Configure PARTID with 100% cassoc settings */ val_mpam_configure_cassoc(msc_index, test_partid, CASSOC_PARTITION_100); + /* Preserve monitor 0 before changing its filter, control, or VALUE. */ + val_mpam_save_csumon_state(msc_index, 0, &saved_csumon); + csumon_state_saved = true; + /* Configure CSU Monitor */ val_mpam_configure_csu_mon(msc_index, test_partid, DEFAULT_PMG, 0); @@ -202,9 +339,6 @@ payload(void) --nrdy_timeout; }; - /* Save the current MPAM2_EL2 settings */ - saved_el2 = val_mpam_reg_read(MPAM2_EL2); - /*** Scenario 1: Buffer copy with 100% CASSOC settings ***/ val_print(DEBUG, "\n Scenario 1: 100 percent CASSOC setting"); @@ -223,6 +357,32 @@ payload(void) val_memory_free_pages(dest_buf, num_pages); if (src_buf != NULL) val_memory_free_pages(src_buf, num_pages); + if (mappings_saved) { + val_mpam_set_partid_mapping(msc_index, test_partid, saved_map_x); + val_mpam_set_partid_mapping(msc_index, test_partid - 1, saved_map_y); + val_mpam_set_partid_mapping(msc_index, test_partid - 2, saved_map_z); + } + restore_status = 0; + if (enable_state_saved) { + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable_x, + 0, test_partid); + restore_status |= val_mpam_msc_endis_partid(msc_index, saved_enable_y, + 0, test_partid - 1); + restore_status |= val_mpam_msc_endis_partid(msc_index, saved_enable_z, + 0, test_partid - 2); + } + if (restore_status) + val_print(ERROR, "\n Failed to restore PARTID enable state"); + if (csumon_state_saved) + val_mpam_restore_csumon_state(msc_index, &saved_csumon); + if (partition_state_saved) { + val_mpam_restore_partition_state(msc_index, &saved_partition_x); + val_mpam_restore_partition_state(msc_index, &saved_partition_y); + val_mpam_restore_partition_state(msc_index, &saved_partition_z); + } + + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); val_set_status(index, RESULT_FAIL(02)); return; } @@ -288,6 +448,32 @@ payload(void) val_memory_free_pages(dest_buf, num_pages); if (src_buf != NULL) val_memory_free_pages(src_buf, num_pages); + if (mappings_saved) { + val_mpam_set_partid_mapping(msc_index, test_partid, saved_map_x); + val_mpam_set_partid_mapping(msc_index, test_partid - 1, saved_map_y); + val_mpam_set_partid_mapping(msc_index, test_partid - 2, saved_map_z); + } + restore_status = 0; + if (enable_state_saved) { + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable_x, + 0, test_partid); + restore_status |= val_mpam_msc_endis_partid(msc_index, saved_enable_y, + 0, test_partid - 1); + restore_status |= val_mpam_msc_endis_partid(msc_index, saved_enable_z, + 0, test_partid - 2); + } + if (restore_status) + val_print(ERROR, "\n Failed to restore PARTID enable state"); + if (csumon_state_saved) + val_mpam_restore_csumon_state(msc_index, &saved_csumon); + if (partition_state_saved) { + val_mpam_restore_partition_state(msc_index, &saved_partition_x); + val_mpam_restore_partition_state(msc_index, &saved_partition_y); + val_mpam_restore_partition_state(msc_index, &saved_partition_z); + } + + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); val_set_status(index, RESULT_FAIL(03)); return; } @@ -353,6 +539,32 @@ payload(void) val_memory_free_pages(dest_buf, num_pages); if (src_buf != NULL) val_memory_free_pages(src_buf, num_pages); + if (mappings_saved) { + val_mpam_set_partid_mapping(msc_index, test_partid, saved_map_x); + val_mpam_set_partid_mapping(msc_index, test_partid - 1, saved_map_y); + val_mpam_set_partid_mapping(msc_index, test_partid - 2, saved_map_z); + } + restore_status = 0; + if (enable_state_saved) { + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable_x, + 0, test_partid); + restore_status |= val_mpam_msc_endis_partid(msc_index, saved_enable_y, + 0, test_partid - 1); + restore_status |= val_mpam_msc_endis_partid(msc_index, saved_enable_z, + 0, test_partid - 2); + } + if (restore_status) + val_print(ERROR, "\n Failed to restore PARTID enable state"); + if (csumon_state_saved) + val_mpam_restore_csumon_state(msc_index, &saved_csumon); + if (partition_state_saved) { + val_mpam_restore_partition_state(msc_index, &saved_partition_x); + val_mpam_restore_partition_state(msc_index, &saved_partition_y); + val_mpam_restore_partition_state(msc_index, &saved_partition_z); + } + + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); val_set_status(index, RESULT_FAIL(04)); return; } @@ -387,9 +599,37 @@ payload(void) test_fail++; } - /* Restore MPAM2_EL2 settings */ +cleanup: + /* Restore MPAM2_EL2 settings and all state modified by this resource. */ val_mpam_reg_write(MPAM2_EL2, saved_el2); + if (mappings_saved) { + val_mpam_set_partid_mapping(msc_index, test_partid, saved_map_x); + val_mpam_set_partid_mapping(msc_index, test_partid - 1, saved_map_y); + val_mpam_set_partid_mapping(msc_index, test_partid - 2, saved_map_z); + } + restore_status = 0; + if (enable_state_saved) { + restore_status = val_mpam_msc_endis_partid(msc_index, saved_enable_x, + 0, test_partid); + restore_status |= val_mpam_msc_endis_partid(msc_index, saved_enable_y, + 0, test_partid - 1); + restore_status |= val_mpam_msc_endis_partid(msc_index, saved_enable_z, + 0, test_partid - 2); + } + if (restore_status) + test_fail++; + if (csumon_state_saved) + val_mpam_restore_csumon_state(msc_index, &saved_csumon); + if (partition_state_saved) { + val_mpam_restore_partition_state(msc_index, &saved_partition_x); + val_mpam_restore_partition_state(msc_index, &saved_partition_y); + val_mpam_restore_partition_state(msc_index, &saved_partition_z); + } + + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + /* Free the buffers to the heap manager */ if (dest_buf != NULL) { val_pe_cache_invalidate_range((uint64_t)dest_buf, BUFFER_SIZE); @@ -404,10 +644,10 @@ payload(void) } } - if (test_skip) - val_set_status(index, RESULT_SKIP(3)); - else if (test_fail) + if (test_fail) val_set_status(index, RESULT_FAIL(5)); + else if (test_skip) + val_set_status(index, RESULT_SKIP(3)); else val_set_status(index, RESULT_PASS); diff --git a/test_pool/mpam/partition005.c b/test_pool/mpam/partition005.c index 9ba4b7d1..86f3d33f 100644 --- a/test_pool/mpam/partition005.c +++ b/test_pool/mpam/partition005.c @@ -144,6 +144,11 @@ payload(void) continue; }; + /* Give each request PARTID a distinct control PARTID when narrowing is supported. */ + if ((val_mpam_prepare_partid(msc_index, partid_x, 0) != ACS_STATUS_PASS) || + (val_mpam_prepare_partid(msc_index, partid_y, 1) != ACS_STATUS_PASS)) + continue; + /* Step 2-3: Configure MPAMCFG_CMAX such that PARTID_X is assigned 75% of the cache and PARTID_Y is assigned 25%, both with SOFTLIM = 0.*/ val_mpam_configure_ccap(msc_index, partid_x, MPAMCFG_CMAX_DISABLE_SOFTLIM, 75); @@ -157,6 +162,8 @@ payload(void) val_mpam_configure_cpor(msc_index, partid_y, 100); /* Allocate memory for source and destination buffers */ + src_buf = NULL; + dest_buf = NULL; src_buf = (void *)val_memory_alloc_pages(num_pages); dest_buf = (void *)val_memory_alloc_pages(num_pages); @@ -167,6 +174,8 @@ payload(void) val_memory_free_pages(dest_buf, num_pages); if (src_buf != NULL) val_memory_free_pages(src_buf, num_pages); + src_buf = NULL; + dest_buf = NULL; return; } @@ -199,6 +208,10 @@ payload(void) /* Step 5 - Trigger buffercpy workload */ val_memcpy(src_buf, dest_buf, BUFFER_SIZE); + val_mem_issue_dsb(); + /* Keep ACS bookkeeping on the original PARTID. */ + val_mpam_reg_write(MPAM2_EL2, saved_el2); + /* Wait for some time before the memcpy settles and counters update */ val_time_delay_ms(TIMEOUT_MEDIUM); @@ -228,6 +241,10 @@ payload(void) } val_memcpy(src_buf, dest_buf, BUFFER_SIZE); + val_mem_issue_dsb(); + /* Restore the original PARTID before disabling PARTID_Y. */ + val_mpam_reg_write(MPAM2_EL2, saved_el2); + /* Wait for some time before the memcpy settles and counters update */ val_time_delay_ms(TIMEOUT_MEDIUM); @@ -271,6 +288,10 @@ payload(void) val_mpam_csumon_enable(msc_index); val_memcpy(src_buf, dest_buf, BUFFER_SIZE); + val_mem_issue_dsb(); + /* Keep ACS bookkeeping on the original PARTID. */ + val_mpam_reg_write(MPAM2_EL2, saved_el2); + /* Wait for some time before the memcpy settles and counters update */ val_time_delay_ms(TIMEOUT_MEDIUM); @@ -298,24 +319,26 @@ payload(void) /* Restore MPAM2_EL2 settings */ val_mpam_reg_write(MPAM2_EL2, saved_el2); + /* Re-enable PARTID_Y before accessing or freeing its workload buffers. */ + val_mpam_msc_endis_partid(msc_index, + 1, // enable PARTID + 0, // NFU not set + partid_y); + /* Free the buffers to the heap manager */ if (dest_buf != NULL) { val_pe_cache_invalidate_range((uint64_t)dest_buf, BUFFER_SIZE); val_mem_issue_dsb(); val_memory_free_pages(dest_buf, num_pages); + dest_buf = NULL; } if (src_buf != NULL) { val_pe_cache_invalidate_range((uint64_t)src_buf, BUFFER_SIZE); val_mem_issue_dsb(); val_memory_free_pages(src_buf, num_pages); + src_buf = NULL; } - /* Re-enable PARTID-Y for the next tests to behave properly */ - val_mpam_msc_endis_partid(msc_index, - 1, // enable PARTID - 0, // NFU not set - partid_y); - } } @@ -330,24 +353,32 @@ payload(void) return; cleanup: + /* Disable CSU Mon */ + val_mpam_csumon_disable(msc_index); + + /* Restore MPAM2_EL2 settings */ + val_mpam_reg_write(MPAM2_EL2, saved_el2); + + /* Ensure cleanup never accesses buffers through a disabled request PARTID. */ + val_mpam_msc_endis_partid(msc_index, + 1, // enable PARTID + 0, // NFU not set + partid_y); + /* Free the buffers to the heap manager */ if (dest_buf != NULL) { val_pe_cache_invalidate_range((uint64_t)dest_buf, BUFFER_SIZE); val_mem_issue_dsb(); val_memory_free_pages(dest_buf, num_pages); + dest_buf = NULL; } if (src_buf != NULL) { val_pe_cache_invalidate_range((uint64_t)src_buf, BUFFER_SIZE); val_mem_issue_dsb(); val_memory_free_pages(src_buf, num_pages); + src_buf = NULL; } - /* Disable CSU Mon */ - val_mpam_csumon_disable(msc_index); - - /* Restore MPAM2_EL2 settings */ - val_mpam_reg_write(MPAM2_EL2, saved_el2); - val_set_status(index, RESULT_FAIL(03)); return; } diff --git a/test_pool/mpam/partition006.c b/test_pool/mpam/partition006.c index f6882116..a39d582e 100644 --- a/test_pool/mpam/partition006.c +++ b/test_pool/mpam/partition006.c @@ -150,6 +150,12 @@ payload(void) continue; } + /* Give each request PARTID a distinct control PARTID when narrowing is supported. */ + if ((val_mpam_prepare_partid(msc_index, partid_x, 0) != ACS_STATUS_PASS) || + (val_mpam_prepare_partid(msc_index, partid_y, 1) != ACS_STATUS_PASS) || + (val_mpam_prepare_partid(msc_index, partid_z, 2) != ACS_STATUS_PASS)) + continue; + /* Step 2: Program PARTID_X with 100% CMAX settings */ val_mpam_configure_ccap(msc_index, partid_x, SOFTLIMIT_DIS, 100); @@ -193,6 +199,10 @@ payload(void) }; val_memcpy(dest_buf, src_buf, BUFFER_SIZE); + val_mem_issue_dsb(); + + /* Keep ACS bookkeeping and monitor accesses on the original PARTID. */ + val_mpam_reg_write(MPAM2_EL2, saved_el2); /* Wait for some time before the memcpy settles and counters update */ val_time_delay_ms(TIMEOUT_MEDIUM); @@ -231,6 +241,10 @@ payload(void) } val_memcpy(dest_buf, src_buf, BUFFER_SIZE); + val_mem_issue_dsb(); + + /* Keep ACS bookkeeping and monitor accesses on the original PARTID. */ + val_mpam_reg_write(MPAM2_EL2, saved_el2); /* Wait for some time before the memcpy settles and counters update */ val_time_delay_ms(TIMEOUT_MEDIUM); @@ -241,6 +255,8 @@ payload(void) /* Read PARTID_X counter after the second buffer copy */ val_mpam_configure_csu_mon(msc_index, partid_x, DEFAULT_PMG, 0); + /* Re-enable the newly selected monitor before reading its value. */ + val_mpam_csumon_enable(msc_index); if (val_mpam_read_csumon(msc_index) > counter[0]) { val_print(ERROR, "\n PARTID_X usage increased after PARTID_Y workload, failing test"); @@ -270,6 +286,9 @@ payload(void) /* Check PARTID_Z cache usage before performing the buffer copy */ val_mpam_configure_csu_mon(msc_index, partid_z, DEFAULT_PMG, 0); + /* Enable the monitor before capturing the PARTID_Z baseline. */ + val_mpam_csumon_enable(msc_index); + counter[2] = val_mpam_read_csumon(msc_index); val_print(DEBUG, "\n PARTID_Z Counter before workload: 0x%x", counter[2]); @@ -283,6 +302,10 @@ payload(void) /* Perform buffer copy */ val_memcpy(dest_buf, src_buf, BUFFER_SIZE); + val_mem_issue_dsb(); + + /* Keep ACS bookkeeping and monitor accesses on the original PARTID. */ + val_mpam_reg_write(MPAM2_EL2, saved_el2); /* Wait for some time before the memcpy settles and counters update */ val_time_delay_ms(TIMEOUT_MEDIUM); @@ -299,6 +322,8 @@ payload(void) /* Read PARTID_X and PARTID_Y counters after PARTID_Z workload */ val_mpam_configure_csu_mon(msc_index, partid_x, DEFAULT_PMG, 0); + /* Re-enable each newly selected monitor before reading its value. */ + val_mpam_csumon_enable(msc_index); if (val_mpam_read_csumon(msc_index) > counter[0]) { val_print(ERROR, "\n PARTID_X usage increased after PARTID_Z workload, failing test"); @@ -310,6 +335,8 @@ payload(void) "\n PARTID_X Counter after PARTID_Z workload: 0x%x", counter[0]); val_mpam_configure_csu_mon(msc_index, partid_y, DEFAULT_PMG, 0); + /* Enable the PARTID_Y monitor selected by the preceding configuration. */ + val_mpam_csumon_enable(msc_index); if (val_mpam_read_csumon(msc_index) > counter[1]) { val_print(ERROR, "\n PARTID_Y usage increased after PARTID_Z workload, failing test"); @@ -332,6 +359,13 @@ payload(void) /* Restore MPAM2_EL2 settings */ val_mpam_reg_write(MPAM2_EL2, saved_el2); + /* Re-enable PARTID_Y before accessing or freeing its workload buffers. */ + val_mpam_msc_endis_partid(msc_index, + 1, // enable PARTID + 0, // NFU not set + partid_y); + is_partid_en = 1; + /* Free the buffers to the heap manager */ if (dest_buf != NULL) { val_pe_cache_invalidate_range((uint64_t)dest_buf, BUFFER_SIZE); @@ -344,12 +378,6 @@ payload(void) val_memory_free_pages(src_buf, num_pages); } - /* Re-enable PARTID-Y for the next tests to behave properly */ - val_mpam_msc_endis_partid(msc_index, - 1, // enable PARTID - 0, // NFU not set - partid_y); - is_partid_en = 1; } } @@ -363,6 +391,10 @@ payload(void) return; cleanup: + /* Stop monitoring and restore the PE's original MPAM context first. */ + val_mpam_csumon_disable(msc_index); + val_mpam_reg_write(MPAM2_EL2, saved_el2); + if (!is_partid_en) { /* Re-enable PARTID-Y for the next tests to behave properly */ val_mpam_msc_endis_partid(msc_index, @@ -383,12 +415,6 @@ payload(void) val_memory_free_pages(src_buf, num_pages); } - /* Disable CSU Mon */ - val_mpam_csumon_disable(msc_index); - - /* Restore MPAM2_EL2 settings */ - val_mpam_reg_write(MPAM2_EL2, saved_el2); - val_set_status(index, RESULT_FAIL(03)); return; } diff --git a/test_pool/mpam/partition007.c b/test_pool/mpam/partition007.c new file mode 100644 index 00000000..3f2fa2f8 --- /dev/null +++ b/test_pool/mpam/partition007.c @@ -0,0 +1,365 @@ +/** @file + * Copyright (c) 2026, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "acs_val.h" +#include "acs_pe.h" +#include "acs_memory.h" +#include "acs_mpam.h" +#include "acs_mpam_reg.h" +#include "val_interface.h" + +#define TEST_NUM (ACS_MPAM_CACHE_TEST_NUM_BASE + 7) +#define TEST_RULE "GFYDF" +#define TEST_DESC "Check PARTID narrowing and monitoring " + +#define CACHE_PERCENTAGE 50U +#define REQ_PARTID_A 1U +#define REQ_PARTID_B 2U +#define INT_PARTID_A 1U +#define INT_PARTID_B 2U + +static uint32_t cpbm_a[MAX_CPBM_WORDS]; +static uint32_t cpbm_b[MAX_CPBM_WORDS]; + +static void +invalidate_buffers(void *src_buf, void *dest_buf, uint32_t buffer_size) +{ + val_pe_cache_invalidate_range((uint64_t)src_buf, buffer_size); + val_pe_cache_invalidate_range((uint64_t)dest_buf, buffer_size); + val_mem_issue_dsb(); +} + +static uint32_t +run_csu_workload(uint32_t msc_index, uint16_t monitor_partid, uint16_t traffic_partid, + void *src_buf, void *dest_buf, uint32_t buffer_size, uint32_t *count) +{ + uint64_t saved_mpam2 = val_mpam_reg_read(MPAM2_EL2); + uint32_t status = 1; + + /* Reuse the buffers, but evict them so every measurement starts cold. */ + invalidate_buffers(src_buf, dest_buf, buffer_size); + + val_mpam_configure_csu_mon(msc_index, monitor_partid, DEFAULT_PMG, 0); + val_mpam_csumon_enable(msc_index); + + /* Apply the test PARTID only to the measured memory traffic. */ + if (val_mpam_program_el2(traffic_partid, DEFAULT_PMG)) + goto cleanup; + val_memcpy(dest_buf, src_buf, buffer_size); + val_mem_issue_dsb(); + val_mpam_reg_write(MPAM2_EL2, saved_mpam2); + + val_time_delay_ms(TIMEOUT_MEDIUM); + *count = val_mpam_read_csumon(msc_index); + status = 0; + +cleanup: + val_mpam_csumon_disable(msc_index); + val_mpam_reg_write(MPAM2_EL2, saved_mpam2); + return status; +} + +static void +payload(void) +{ + uint32_t pe_index = val_pe_get_index_mpid(val_pe_get_mpid()); + uint32_t msc_count = val_mpam_get_msc_count(); + uint32_t llc_index = val_cache_get_llc_index(); + uint64_t cache_id; + uint64_t cache_size; + uint64_t buffer_size64; + uint64_t num_pages64; + uint32_t buffer_size; + uint32_t page_size; + uint32_t num_pages; + void *src_buf; + void *dest_buf; + uint32_t msc_index; + uint32_t rsrc_index; + uint32_t rsrc_count; + uint32_t cpbm_width; + uint32_t cpbm_words; + uint32_t saved_part_sel; + uint32_t saved_cmax_a; + uint32_t saved_cmax_b; + uint16_t saved_map_a; + uint16_t saved_map_b; + uint32_t saved_mon_sel; + uint32_t status; + bool endis_supported; + bool saved_enable_a; + bool saved_enable_b; + MPAM_CSUMON_STATE saved_csumon; + uint64_t saved_mpam2; + uint32_t count_allowed; + uint32_t count_blocked; + uint32_t count_nonmatch; + uint32_t count_match; + uint32_t test_fail = 0; + uint32_t test_skip = 1; + uint64_t pe_max_partid; + + /* The PE must be able to generate both fixed request PARTID labels. */ + pe_max_partid = (val_mpam_reg_read(MPAMIDR_EL1) >> MPAMIDR_PARTID_MAX_SHIFT) & + MPAMIDR_PARTID_MAX_MASK; + if (pe_max_partid < REQ_PARTID_B) { + val_set_status(pe_index, RESULT_SKIP(03)); + return; + } + + if (llc_index == CACHE_TABLE_EMPTY) { + val_set_status(pe_index, RESULT_SKIP(01)); + return; + } + + cache_id = val_cache_get_info(CACHE_ID, llc_index); + cache_size = val_cache_get_info(CACHE_SIZE, llc_index); + if ((cache_id == INVALID_CACHE_INFO) || + (cache_size == INVALID_CACHE_INFO) || + (cache_size < 2U)) { + val_set_status(pe_index, RESULT_SKIP(02)); + return; + } + + /* Calculate the percentage without overflowing cache_size. */ + buffer_size64 = ((cache_size / 100U) * CACHE_PERCENTAGE) + + (((cache_size % 100U) * CACHE_PERCENTAGE) / 100U); + if ((buffer_size64 == 0U) || (buffer_size64 > ACS_UINT32_MAX)) { + val_set_status(pe_index, RESULT_SKIP(02)); + return; + } + buffer_size = (uint32_t)buffer_size64; + + page_size = val_memory_page_size(); + if (page_size == 0U) { + val_set_status(pe_index, RESULT_SKIP(02)); + return; + } + + num_pages64 = (buffer_size64 + page_size - 1U) / page_size; + /* Validate the allocator page count before calculating its byte capacity. */ + if ((num_pages64 == 0U) || (num_pages64 > ACS_UINT32_MAX) || + (num_pages64 > (~(uint64_t)0 / page_size)) || + ((num_pages64 * page_size) < buffer_size64)) { + val_set_status(pe_index, RESULT_SKIP(02)); + return; + } + num_pages = (uint32_t)num_pages64; + + /* One allocation is sufficient; cache invalidation makes each workload cold. */ + src_buf = val_memory_alloc_pages(num_pages); + dest_buf = val_memory_alloc_pages(num_pages); + if ((src_buf == NULL) || (dest_buf == NULL)) { + val_print(ERROR, "\n Mem allocation failed"); + if (src_buf != NULL) + val_memory_free_pages(src_buf, num_pages); + if (dest_buf != NULL) + val_memory_free_pages(dest_buf, num_pages); + val_set_status(pe_index, RESULT_FAIL(01)); + return; + } + val_memory_set(src_buf, buffer_size, 0xA5U); + + /* Find an LLC resource that can exercise narrowing through CPOR and CSUMON. */ + for (msc_index = 0; msc_index < msc_count; msc_index++) { + if (!val_mpam_msc_supports_partid_nrw(msc_index) || + (val_mpam_get_max_partid(msc_index) < REQ_PARTID_B) || + (val_mpam_get_max_intpartid(msc_index) < INT_PARTID_B)) + continue; + + rsrc_count = val_mpam_get_info(MPAM_MSC_RSRC_COUNT, msc_index, 0); + for (rsrc_index = 0; rsrc_index < rsrc_count; rsrc_index++) { + if ((val_mpam_get_info(MPAM_MSC_RSRC_TYPE, msc_index, rsrc_index) != + MPAM_RSRC_TYPE_PE_CACHE) || + (val_mpam_get_info(MPAM_MSC_RSRC_DESC1, msc_index, rsrc_index) != cache_id)) + continue; + + /* Save selectors before changing the resource instance. */ + saved_part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + saved_mon_sel = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + + /* Select the same resource instance for partition controls and monitoring. */ + if (val_mpam_msc_supports_ris(msc_index)) + val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); + + /* Partitioning and monitoring capabilities are specific to the selected RIS. */ + if (!val_mpam_supports_cpor(msc_index) || + !val_mpam_supports_csumon(msc_index) || + (val_mpam_get_csumon_count(msc_index) == 0U)) { + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + continue; + } + + cpbm_width = val_mpam_get_cpbm_width(msc_index); + if ((cpbm_width == 0U) || (cpbm_width > (MAX_CPBM_WORDS * 32U))) { + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + continue; + } + cpbm_words = (cpbm_width + 31U) / 32U; + + test_skip = 0; + /* Preserve all architectural state changed by the test. */ + saved_mpam2 = val_mpam_reg_read(MPAM2_EL2); + saved_map_a = val_mpam_get_partid_mapping(msc_index, REQ_PARTID_A); + saved_map_b = val_mpam_get_partid_mapping(msc_index, REQ_PARTID_B); + endis_supported = val_mpam_msc_supports_partid_endis(msc_index); + saved_enable_a = endis_supported ? + val_mpam_msc_partid_is_enabled(msc_index, REQ_PARTID_A) : true; + saved_enable_b = endis_supported ? + val_mpam_msc_partid_is_enabled(msc_index, REQ_PARTID_B) : true; + val_mpam_read_cpbm(msc_index, INT_PARTID_A, cpbm_a, cpbm_words); + val_mpam_read_cpbm(msc_index, INT_PARTID_B, cpbm_b, cpbm_words); + if (val_mpam_supports_ccap(msc_index)) { + val_mpam_select_partid(msc_index, INT_PARTID_A, true); + saved_cmax_a = val_mpam_mmr_read(msc_index, REG_MPAMCFG_CMAX); + val_mpam_select_partid(msc_index, INT_PARTID_B, true); + saved_cmax_b = val_mpam_mmr_read(msc_index, REG_MPAMCFG_CMAX); + } + val_mpam_save_csumon_state(msc_index, 0, &saved_csumon); + + /* + * Give the two internal PARTIDs opposite CPBM settings. Keep CMAX + * unrestricted, when present, so CPBM is the differentiating control. + */ + val_mpam_configure_intpartid_cpor(msc_index, INT_PARTID_A, 100); + val_mpam_configure_intpartid_cpor(msc_index, INT_PARTID_B, 0); + if (val_mpam_supports_ccap(msc_index)) { + val_mpam_configure_intpartid_ccap(msc_index, INT_PARTID_A, 0, 100); + val_mpam_configure_intpartid_ccap(msc_index, INT_PARTID_B, 0, 100); + } + /* GFYDF: first map request A to the internal PARTID with full CPBM. */ + val_mpam_set_partid_mapping(msc_index, REQ_PARTID_A, INT_PARTID_A); + val_mpam_set_partid_mapping(msc_index, REQ_PARTID_B, INT_PARTID_B); + status = 0; + if (endis_supported) { + status = val_mpam_msc_endis_partid(msc_index, 1, 0, REQ_PARTID_A); + status |= val_mpam_msc_endis_partid(msc_index, 1, 0, REQ_PARTID_B); + } + if (status) { + test_fail++; + goto restore; + } + + if (val_mpam_get_partid_mapping(msc_index, REQ_PARTID_A) != INT_PARTID_A) { + val_print(ERROR, "\n Request PARTID mapping readback failed"); + test_fail++; + goto restore; + } + + if (run_csu_workload(msc_index, REQ_PARTID_A, REQ_PARTID_A, + src_buf, dest_buf, buffer_size, &count_allowed)) { + test_fail++; + goto restore; + } + if (count_allowed == 0U) { + val_print(ERROR, "\n No occupancy with enabled internal CPBM"); + test_fail++; + goto restore; + } + + /* Remap only request A; lower occupancy proves the new internal control applies. */ + val_mpam_set_partid_mapping(msc_index, REQ_PARTID_A, INT_PARTID_B); + if (run_csu_workload(msc_index, REQ_PARTID_A, REQ_PARTID_A, + src_buf, dest_buf, buffer_size, &count_blocked)) { + test_fail++; + goto restore; + } + if (count_blocked >= count_allowed) { + val_print(ERROR, "\n Remapping did not change cache occupancy"); + test_fail++; + goto restore; + } + + /* + * ZGGMX: requests A and B share one internal PARTID, but CSUMON must + * continue to distinguish their original request PARTID labels. + */ + val_mpam_set_partid_mapping(msc_index, REQ_PARTID_A, INT_PARTID_A); + val_mpam_set_partid_mapping(msc_index, REQ_PARTID_B, INT_PARTID_A); + if (run_csu_workload(msc_index, REQ_PARTID_A, REQ_PARTID_B, + src_buf, dest_buf, buffer_size, &count_nonmatch)) { + test_fail++; + goto restore; + } + + if (run_csu_workload(msc_index, REQ_PARTID_A, REQ_PARTID_A, + src_buf, dest_buf, buffer_size, &count_match)) { + test_fail++; + goto restore; + } + if (count_match <= count_nonmatch) { + val_print(ERROR, "\n Monitor did not distinguish request PARTIDs"); + test_fail++; + } + +restore: + /* Restore mappings, partition controls, monitor state, and MPAM2_EL2. */ + val_mpam_csumon_disable(msc_index); + val_mpam_write_cpbm(msc_index, INT_PARTID_A, cpbm_a, cpbm_words); + val_mpam_write_cpbm(msc_index, INT_PARTID_B, cpbm_b, cpbm_words); + if (val_mpam_supports_ccap(msc_index)) { + val_mpam_select_partid(msc_index, INT_PARTID_A, true); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_CMAX, saved_cmax_a); + val_mpam_select_partid(msc_index, INT_PARTID_B, true); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_CMAX, saved_cmax_b); + } + val_mpam_set_partid_mapping(msc_index, REQ_PARTID_A, saved_map_a); + val_mpam_set_partid_mapping(msc_index, REQ_PARTID_B, saved_map_b); + status = 0; + if (endis_supported) { + status = val_mpam_msc_endis_partid(msc_index, saved_enable_a, + 0, REQ_PARTID_A); + status |= val_mpam_msc_endis_partid(msc_index, saved_enable_b, + 0, REQ_PARTID_B); + } + if (status) + test_fail++; + val_mpam_restore_csumon_state(msc_index, &saved_csumon); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + val_mpam_reg_write(MPAM2_EL2, saved_mpam2); + } + } + + invalidate_buffers(src_buf, dest_buf, buffer_size); + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + + if (test_skip) + val_set_status(pe_index, RESULT_SKIP(03)); + else if (test_fail) + val_set_status(pe_index, RESULT_FAIL(01)); + else + val_set_status(pe_index, RESULT_PASS); +} + +uint32_t +partition007_entry(uint32_t num_pe) +{ + uint32_t status; + + num_pe = 1; + val_log_context((char8_t *)__FILE__, (char8_t *)__func__, __LINE__); + status = val_initialize_test(TEST_NUM, TEST_DESC, num_pe); + if (status != ACS_STATUS_SKIP) + val_run_test_payload(TEST_NUM, num_pe, payload, 0); + status = val_check_for_error(TEST_NUM, num_pe, TEST_RULE); + val_report_status(0, ACS_END(TEST_NUM), TEST_RULE); + return status; +} diff --git a/test_pool/mpam/partition008.c b/test_pool/mpam/partition008.c new file mode 100644 index 00000000..4807acc5 --- /dev/null +++ b/test_pool/mpam/partition008.c @@ -0,0 +1,377 @@ +/** @file + * Copyright (c) 2026, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "acs_val.h" +#include "acs_pe.h" +#include "acs_memory.h" +#include "acs_mpam.h" +#include "acs_mpam_reg.h" +#include "val_interface.h" + +#define TEST_NUM (ACS_MPAM_CACHE_TEST_NUM_BASE + 8) +#define TEST_RULE "CSLHH" +#define TEST_DESC "Check narrowed request PARTID disable " + +#define CACHE_PERCENTAGE 50U +#define REQ_PARTID_A 1U +#define REQ_PARTID_B 2U +#define INT_PARTID_A 1U + +static uint32_t cpbm_saved[MAX_CPBM_WORDS]; +static uint32_t cpbm_check[MAX_CPBM_WORDS]; + +static void +invalidate_buffers(void *src_buf, void *dest_buf, uint32_t buffer_size) +{ + val_pe_cache_invalidate_range((uint64_t)src_buf, buffer_size); + val_pe_cache_invalidate_range((uint64_t)dest_buf, buffer_size); + val_mem_issue_dsb(); +} + +static uint32_t +run_csu_workload(uint32_t msc_index, uint16_t monitor_partid, uint16_t traffic_partid, + void *src_buf, void *dest_buf, uint32_t buffer_size, uint32_t *count) +{ + uint64_t saved_mpam2 = val_mpam_reg_read(MPAM2_EL2); + uint32_t status = 1; + + /* Reuse the buffers, but evict them so every measurement starts cold. */ + invalidate_buffers(src_buf, dest_buf, buffer_size); + + val_mpam_configure_csu_mon(msc_index, monitor_partid, DEFAULT_PMG, 0); + val_mpam_csumon_enable(msc_index); + + /* Apply the test PARTID only to the measured memory traffic. */ + if (val_mpam_program_el2(traffic_partid, DEFAULT_PMG)) + goto cleanup; + val_memcpy(dest_buf, src_buf, buffer_size); + val_mem_issue_dsb(); + val_mpam_reg_write(MPAM2_EL2, saved_mpam2); + + val_time_delay_ms(TIMEOUT_MEDIUM); + *count = val_mpam_read_csumon(msc_index); + status = 0; + +cleanup: + val_mpam_csumon_disable(msc_index); + val_mpam_reg_write(MPAM2_EL2, saved_mpam2); + return status; +} + +static void +payload(void) +{ + uint32_t pe_index = val_pe_get_index_mpid(val_pe_get_mpid()); + uint32_t msc_count = val_mpam_get_msc_count(); + uint32_t llc_index = val_cache_get_llc_index(); + uint64_t cache_id; + uint64_t cache_size; + uint64_t buffer_size64; + uint64_t num_pages64; + uint32_t buffer_size; + uint32_t page_size; + uint32_t num_pages; + void *src_buf; + void *dest_buf; + uint32_t msc_index; + uint32_t rsrc_index; + uint32_t rsrc_count; + uint32_t cpbm_width; + uint32_t cpbm_words; + uint32_t saved_part_sel; + uint32_t saved_cmax; + uint16_t saved_map_a; + uint16_t saved_map_b; + uint32_t saved_mon_sel; + bool saved_enable_a; + bool saved_enable_b; + MPAM_CSUMON_STATE saved_csumon; + uint64_t saved_mpam2; + uint32_t count_enabled; + uint32_t count_disabled; + uint32_t count_peer; + uint32_t count_reenabled; + uint32_t status; + uint32_t word; + uint32_t last_bits; + uint32_t expected; + uint32_t test_fail = 0; + uint32_t test_skip = 1; + uint64_t pe_max_partid; + + /* The PE must be able to generate both fixed request PARTID labels. */ + pe_max_partid = (val_mpam_reg_read(MPAMIDR_EL1) >> MPAMIDR_PARTID_MAX_SHIFT) & + MPAMIDR_PARTID_MAX_MASK; + if (pe_max_partid < REQ_PARTID_B) { + val_set_status(pe_index, RESULT_SKIP(03)); + return; + } + + /* CSLHH requires both narrowing and per-request enable control. */ + for (msc_index = 0; msc_index < msc_count; msc_index++) { + if (val_mpam_msc_supports_partid_nrw(msc_index) && + val_mpam_msc_supports_partid_endis(msc_index)) + break; + } + if (msc_index == msc_count) { + val_set_status(pe_index, RESULT_SKIP(03)); + return; + } + + if (llc_index == CACHE_TABLE_EMPTY) { + val_set_status(pe_index, RESULT_SKIP(01)); + return; + } + + cache_id = val_cache_get_info(CACHE_ID, llc_index); + cache_size = val_cache_get_info(CACHE_SIZE, llc_index); + if ((cache_id == INVALID_CACHE_INFO) || + (cache_size == INVALID_CACHE_INFO) || + (cache_size < 2U)) { + val_set_status(pe_index, RESULT_SKIP(02)); + return; + } + + /* Calculate the percentage without overflowing cache_size. */ + buffer_size64 = ((cache_size / 100U) * CACHE_PERCENTAGE) + + (((cache_size % 100U) * CACHE_PERCENTAGE) / 100U); + if ((buffer_size64 == 0U) || (buffer_size64 > ACS_UINT32_MAX)) { + val_set_status(pe_index, RESULT_SKIP(02)); + return; + } + buffer_size = (uint32_t)buffer_size64; + + page_size = val_memory_page_size(); + if (page_size == 0U) { + val_set_status(pe_index, RESULT_SKIP(02)); + return; + } + + num_pages64 = (buffer_size64 + page_size - 1U) / page_size; + /* Validate the allocator page count before calculating its byte capacity. */ + if ((num_pages64 == 0U) || (num_pages64 > ACS_UINT32_MAX) || + (num_pages64 > (~(uint64_t)0 / page_size)) || + ((num_pages64 * page_size) < buffer_size64)) { + val_set_status(pe_index, RESULT_SKIP(02)); + return; + } + num_pages = (uint32_t)num_pages64; + + /* One allocation is sufficient; cache invalidation makes each workload cold. */ + src_buf = val_memory_alloc_pages(num_pages); + dest_buf = val_memory_alloc_pages(num_pages); + if ((src_buf == NULL) || (dest_buf == NULL)) { + val_print(ERROR, "\n Mem allocation failed"); + if (src_buf != NULL) + val_memory_free_pages(src_buf, num_pages); + if (dest_buf != NULL) + val_memory_free_pages(dest_buf, num_pages); + val_set_status(pe_index, RESULT_FAIL(01)); + return; + } + val_memory_set(src_buf, buffer_size, 0xA5U); + + /* Find an LLC resource supporting narrowing and request-PARTID enable control. */ + for (msc_index = 0; msc_index < msc_count; msc_index++) { + if (!val_mpam_msc_supports_partid_nrw(msc_index) || + !val_mpam_msc_supports_partid_endis(msc_index) || + (val_mpam_get_max_partid(msc_index) < REQ_PARTID_B) || + (val_mpam_get_max_intpartid(msc_index) < INT_PARTID_A)) + continue; + + rsrc_count = val_mpam_get_info(MPAM_MSC_RSRC_COUNT, msc_index, 0); + for (rsrc_index = 0; rsrc_index < rsrc_count; rsrc_index++) { + if ((val_mpam_get_info(MPAM_MSC_RSRC_TYPE, msc_index, rsrc_index) != + MPAM_RSRC_TYPE_PE_CACHE) || + (val_mpam_get_info(MPAM_MSC_RSRC_DESC1, msc_index, rsrc_index) != cache_id)) + continue; + + /* Save selectors before changing the resource instance. */ + saved_part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + saved_mon_sel = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + + /* Select the same resource instance for partition controls and monitoring. */ + if (val_mpam_msc_supports_ris(msc_index)) + val_mpam_memory_configure_ris_sel(msc_index, rsrc_index); + + /* Partitioning and monitoring capabilities are specific to the selected RIS. */ + if (!val_mpam_supports_cpor(msc_index) || + !val_mpam_supports_csumon(msc_index) || + (val_mpam_get_csumon_count(msc_index) == 0U)) { + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + continue; + } + + cpbm_width = val_mpam_get_cpbm_width(msc_index); + if ((cpbm_width == 0U) || (cpbm_width > (MAX_CPBM_WORDS * 32U))) { + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + continue; + } + cpbm_words = (cpbm_width + 31U) / 32U; + + test_skip = 0; + /* Preserve all architectural state changed by the test. */ + saved_mpam2 = val_mpam_reg_read(MPAM2_EL2); + saved_map_a = val_mpam_get_partid_mapping(msc_index, REQ_PARTID_A); + saved_map_b = val_mpam_get_partid_mapping(msc_index, REQ_PARTID_B); + saved_enable_a = val_mpam_msc_partid_is_enabled(msc_index, REQ_PARTID_A); + saved_enable_b = val_mpam_msc_partid_is_enabled(msc_index, REQ_PARTID_B); + val_mpam_read_cpbm(msc_index, INT_PARTID_A, cpbm_saved, cpbm_words); + if (val_mpam_supports_ccap(msc_index)) { + val_mpam_select_partid(msc_index, INT_PARTID_A, true); + saved_cmax = val_mpam_mmr_read(msc_index, REG_MPAMCFG_CMAX); + } + val_mpam_save_csumon_state(msc_index, 0, &saved_csumon); + + /* + * Both requests share an unrestricted internal control. Disabling + * one request must not modify that shared control or affect its peer. + */ + val_mpam_configure_intpartid_cpor(msc_index, INT_PARTID_A, 100); + if (val_mpam_supports_ccap(msc_index)) + val_mpam_configure_intpartid_ccap(msc_index, INT_PARTID_A, 0, 100); + val_mpam_set_partid_mapping(msc_index, REQ_PARTID_A, INT_PARTID_A); + val_mpam_set_partid_mapping(msc_index, REQ_PARTID_B, INT_PARTID_A); + status = val_mpam_msc_endis_partid(msc_index, 1, 0, REQ_PARTID_A); + status |= val_mpam_msc_endis_partid(msc_index, 1, 0, REQ_PARTID_B); + if (status) { + test_fail++; + goto restore; + } + + /* Establish normal enabled occupancy for request A as the baseline. */ + if (run_csu_workload(msc_index, REQ_PARTID_A, REQ_PARTID_A, + src_buf, dest_buf, buffer_size, &count_enabled)) { + test_fail++; + goto restore; + } + if (count_enabled == 0U) { + val_print(ERROR, "\n Enabled request PARTID has no cache occupancy"); + test_fail++; + goto restore; + } + + /* CSLHH: disable request A while request B remains enabled on the same intPARTID. */ + status = val_mpam_msc_endis_partid(msc_index, 0, 0, REQ_PARTID_A); + if (status) { + test_fail++; + goto restore; + } + if (run_csu_workload(msc_index, REQ_PARTID_A, REQ_PARTID_A, + src_buf, dest_buf, buffer_size, &count_disabled)) { + test_fail++; + goto restore; + } + if (count_disabled >= count_enabled) { + val_print(ERROR, "\n Disabled request PARTID was not deprioritized"); + test_fail++; + goto restore; + } + + /* The enabled peer must retain use of the shared internal control. */ + if (run_csu_workload(msc_index, REQ_PARTID_B, REQ_PARTID_B, + src_buf, dest_buf, buffer_size, &count_peer)) { + test_fail++; + goto restore; + } + if (count_peer == 0U) { + val_print(ERROR, "\n Enabled peer request has no cache occupancy"); + test_fail++; + goto restore; + } + + /* Disabling a request PARTID must not clear the shared internal CPBM. */ + val_mpam_read_cpbm(msc_index, INT_PARTID_A, cpbm_check, cpbm_words); + last_bits = cpbm_width & 31U; + for (word = 0; word < cpbm_words; word++) { + expected = ACS_UINT32_MAX; + if ((word == (cpbm_words - 1U)) && (last_bits != 0U)) + expected = (1U << last_bits) - 1U; + if (cpbm_check[word] != expected) { + val_print(ERROR, "\n Disabling request changed internal CPBM"); + test_fail++; + goto restore; + } + } + + /* Re-enable request A and verify that its normal cache behavior recovers. */ + status = val_mpam_msc_endis_partid(msc_index, 1, 0, REQ_PARTID_A); + if (status) { + test_fail++; + goto restore; + } + if (run_csu_workload(msc_index, REQ_PARTID_A, REQ_PARTID_A, + src_buf, dest_buf, buffer_size, &count_reenabled)) { + test_fail++; + goto restore; + } + if (count_reenabled <= count_disabled) { + val_print(ERROR, "\n Re-enabled request did not recover cache occupancy"); + test_fail++; + } + +restore: + /* Restore mappings, request enable states, controls, and monitor state. */ + val_mpam_csumon_disable(msc_index); + val_mpam_write_cpbm(msc_index, INT_PARTID_A, cpbm_saved, cpbm_words); + if (val_mpam_supports_ccap(msc_index)) { + val_mpam_select_partid(msc_index, INT_PARTID_A, true); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_CMAX, saved_cmax); + } + val_mpam_set_partid_mapping(msc_index, REQ_PARTID_A, saved_map_a); + val_mpam_set_partid_mapping(msc_index, REQ_PARTID_B, saved_map_b); + status = val_mpam_msc_endis_partid(msc_index, saved_enable_a, + 0, REQ_PARTID_A); + status |= val_mpam_msc_endis_partid(msc_index, saved_enable_b, + 0, REQ_PARTID_B); + if (status) + test_fail++; + val_mpam_restore_csumon_state(msc_index, &saved_csumon); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, saved_mon_sel); + val_mpam_reg_write(MPAM2_EL2, saved_mpam2); + } + } + + invalidate_buffers(src_buf, dest_buf, buffer_size); + val_memory_free_pages(src_buf, num_pages); + val_memory_free_pages(dest_buf, num_pages); + + if (test_skip) + val_set_status(pe_index, RESULT_SKIP(03)); + else if (test_fail) + val_set_status(pe_index, RESULT_FAIL(01)); + else + val_set_status(pe_index, RESULT_PASS); +} + +uint32_t +partition008_entry(uint32_t num_pe) +{ + uint32_t status; + + num_pe = 1; + val_log_context((char8_t *)__FILE__, (char8_t *)__func__, __LINE__); + status = val_initialize_test(TEST_NUM, TEST_DESC, num_pe); + if (status != ACS_STATUS_SKIP) + val_run_test_payload(TEST_NUM, num_pe, payload, 0); + status = val_check_for_error(TEST_NUM, num_pe, TEST_RULE); + val_report_status(0, ACS_END(TEST_NUM), TEST_RULE); + return status; +} diff --git a/tools/cmake/infra/mpam_test.txt b/tools/cmake/infra/mpam_test.txt index 39d7bb2b..23e043af 100644 --- a/tools/cmake/infra/mpam_test.txt +++ b/tools/cmake/infra/mpam_test.txt @@ -30,6 +30,8 @@ partition003.c partition004.c partition005.c partition006.c +partition007.c +partition008.c monitor001.c monitor002.c monitor003.c diff --git a/val/include/acs_mpam.h b/val/include/acs_mpam.h index a3f7ad0f..b69cc728 100644 --- a/val/include/acs_mpam.h +++ b/val/include/acs_mpam.h @@ -75,8 +75,31 @@ typedef enum { #define MBWU_SHORT_CHECK 0U #define MAX_CPBM_WIDTH 32768 +#define MAX_CPBM_WORDS ((MAX_CPBM_WIDTH + 31U) / 32U) #define MAX_BWPBM_WIDTH 4096 +/* Register state for one cache-storage usage monitor instance. */ +typedef struct { + uint32_t selector; + uint32_t selected_monitor; + uint32_t filter; + uint32_t control; + uint32_t value; + bool value_writable; +} MPAM_CSUMON_STATE; + +typedef struct { + uint32_t selector; + uint32_t control_selector; + uint32_t *cpbm; + uint32_t cpbm_words; + uint32_t cmax; + uint32_t cassoc; + bool cpbm_valid; + bool cmax_valid; + bool cassoc_valid; +} MPAM_PARTITION_STATE; + /* Resource-group hash configuration; the slot count is kept as a power of two. */ #define MPAM_RSRC_HASH_WORD_SHIFT 32U #define MPAM_RSRC_HASH_SLOT_SCALE 2U @@ -151,11 +174,26 @@ uint32_t val_mpam_mbwu_supports_long(uint32_t msc_index); uint32_t val_mpam_mbwu_supports_lwd(uint32_t msc_index); uint32_t val_mpam_get_max_partid(uint32_t msc_index); uint16_t val_mpam_get_max_intpartid(uint32_t msc_index); +void val_mpam_select_partid(uint32_t msc_index, uint16_t partid, bool internal); +uint16_t val_mpam_get_partid_mapping(uint32_t msc_index, uint16_t req_partid); +void val_mpam_set_partid_mapping(uint32_t msc_index, uint16_t req_partid, + uint16_t int_partid); +uint32_t val_mpam_prepare_partid(uint32_t msc_index, uint16_t req_partid, + uint16_t int_partid); +bool val_mpam_msc_partid_is_enabled(uint32_t msc_index, uint16_t req_partid); +void val_mpam_read_cpbm(uint32_t msc_index, uint16_t int_partid, + uint32_t *cpbm, uint32_t words); +void val_mpam_write_cpbm(uint32_t msc_index, uint16_t int_partid, + const uint32_t *cpbm, uint32_t words); uint32_t val_mpam_msc_get_version(uint32_t msc_index); uint32_t val_mpam_get_max_pmg(uint32_t msc_index); void val_mpam_configure_cpor(uint32_t msc_index, uint16_t partid, uint32_t cpbm_percentage); +void val_mpam_configure_intpartid_cpor(uint32_t msc_index, uint16_t int_partid, + uint32_t cpbm_percentage); void val_mpam_configure_ccap(uint32_t msc_index, uint16_t partid, uint8_t softlim, uint32_t ccap_percentage); +void val_mpam_configure_intpartid_ccap(uint32_t msc_index, uint16_t int_partid, + uint8_t softlim, uint32_t ccap_percentage); void val_mpam_configure_cassoc(uint32_t msc_index, uint16_t partid, uint32_t cassoc_percentage); void val_mpam_configure_cmin(uint32_t msc_index, uint16_t partid, uint32_t cmin_percentage); @@ -166,6 +204,13 @@ void val_mpam_msc_configure_mbwmax(uint32_t msc_index, uint16_t partid, uint32_t val_mpam_get_cpbm_width(uint32_t msc_index); uint32_t val_mpam_get_mbwpbm_width(uint32_t msc_index); void val_mpam_configure_csu_mon(uint32_t msc_index, uint16_t partid, uint8_t pmg, uint16_t mon_sel); +void val_mpam_save_csumon_state(uint32_t msc_index, uint16_t mon_sel, + MPAM_CSUMON_STATE *state); +void val_mpam_restore_csumon_state(uint32_t msc_index, const MPAM_CSUMON_STATE *state); +bool val_mpam_save_partition_state(uint32_t msc_index, uint16_t req_partid, + MPAM_PARTITION_STATE *state); +void val_mpam_restore_partition_state(uint32_t msc_index, + MPAM_PARTITION_STATE *state); void val_mpam_csumon_enable(uint32_t msc_index); void val_mpam_csumon_disable(uint32_t msc_index); uint32_t val_mpam_read_csumon(uint32_t msc_index); @@ -208,6 +253,8 @@ uint32_t partition002_entry(uint32_t num_pe); uint32_t partition004_entry(uint32_t num_pe); uint32_t partition005_entry(uint32_t num_pe); uint32_t partition006_entry(uint32_t num_pe); +uint32_t partition007_entry(uint32_t num_pe); +uint32_t partition008_entry(uint32_t num_pe); uint32_t mem001_entry(uint32_t num_pe); uint32_t mem002_entry(uint32_t num_pe); diff --git a/val/include/acs_mpam_reg.h b/val/include/acs_mpam_reg.h index 59aa931f..4f9bf980 100644 --- a/val/include/acs_mpam_reg.h +++ b/val/include/acs_mpam_reg.h @@ -62,6 +62,7 @@ #define REG_MPAMCFG_MBW_MIN 0x0200 #define REG_MPAMCFG_EN 0x0300 #define REG_MPAMCFG_DIS 0x0310 +#define REG_MPAMCFG_EN_FLAGS 0x0320 #define REG_MPAMCFG_MBW_MAX 0x0208 #define REG_MPAMCFG_INTPARTID 0x0600 #define REG_MPAMCFG_CPBM 0x1000 @@ -174,6 +175,8 @@ BITFIELD_DECL(uint32_t, MPAMCFG_DIS_NFU, 31, 31) /* MPAMCFG_INTPARTID bit definitions */ #define MPAMCFG_INTPARTID_INTPARTID_INTERNAL_SHIFT 16 +BITFIELD_DECL(uint32_t, MPAMCFG_INTPARTID_INTPARTID, 15, 0) +BITFIELD_DECL(uint32_t, MPAMCFG_INTPARTID_INTERNAL, 16, 16) /* MSMON_CFG_MBWU_CTL bit definitions */ #define MBWU_CTL_ENABLE_MATCH_PARTID_SHIFT 16 diff --git a/val/include/rule_based_execution_enum.h b/val/include/rule_based_execution_enum.h index 9005f1f4..bae38270 100644 --- a/val/include/rule_based_execution_enum.h +++ b/val/include/rule_based_execution_enum.h @@ -1145,6 +1145,8 @@ typedef enum { PARTITION004_ENTRY, PARTITION005_ENTRY, PARTITION006_ENTRY, + PARTITION007_ENTRY, + PARTITION008_ENTRY, MONITOR007_ENTRY, MONITOR008_ENTRY, ERROR001_ENTRY, diff --git a/val/include/val_interface.h b/val/include/val_interface.h index 62ab82b1..dcbd9418 100644 --- a/val/include/val_interface.h +++ b/val/include/val_interface.h @@ -803,7 +803,7 @@ uint32_t val_mpam_msc_generate_psr_error(uint32_t msc_index); void val_mpam_msc_generate_msr_error(uint32_t msc_index, uint16_t mon_count); uint32_t val_mpam_msc_generate_por_error(uint32_t msc_index); uint32_t val_mpam_msc_generate_pmgor_error(uint32_t msc_index); -void val_mpam_msc_generate_msmon_config_error(uint32_t msc_index, uint16_t mon_count); +uint32_t val_mpam_msc_generate_msmon_config_error(uint32_t msc_index, uint16_t mon_count); void val_mpam_msc_generate_msmon_oflow_error(uint32_t msc_index, uint16_t mon_count); void val_mpam_msc_trigger_intr(uint32_t msc_index); uint32_t val_mpam_msc_request_msi(uint32_t msc_index, uint32_t device_id, uint32_t its_id, @@ -858,6 +858,8 @@ uint32_t partition003_entry(uint32_t num_pe); uint32_t partition004_entry(uint32_t num_pe); uint32_t partition005_entry(uint32_t num_pe); uint32_t partition006_entry(uint32_t num_pe); +uint32_t partition007_entry(uint32_t num_pe); +uint32_t partition008_entry(uint32_t num_pe); uint32_t feat001_entry(uint32_t num_pe); // MPAM PARTID EN/DIS feature check test diff --git a/val/src/acs_mpam.c b/val/src/acs_mpam.c index 3c07b853..7de9362b 100644 --- a/val/src/acs_mpam.c +++ b/val/src/acs_mpam.c @@ -62,6 +62,7 @@ mpam_reg_offset_name(uint32_t reg_offset) case REG_MPAMCFG_MBW_MAX: return "MPAMCFG_MBW_MAX"; case REG_MPAMCFG_EN: return "MPAMCFG_EN"; case REG_MPAMCFG_DIS: return "MPAMCFG_DIS"; + case REG_MPAMCFG_EN_FLAGS: return "MPAMCFG_EN_FLAGS"; case REG_MPAMCFG_INTPARTID: return "MPAMCFG_INTPARTID"; case REG_MPAMCFG_CPBM: return "MPAMCFG_CPBM"; case REG_MPAMCFG_MBW_PBM: return "MPAMCFG_MBW_PBM"; @@ -1375,9 +1376,9 @@ val_mpam_create_info_table(uint64_t *mpam_info_table) mpam_create_rsrc_groups(); val_print(INFO, - "\n MPAM_INFO: Number of MSC nodes : %d", g_mpam_info_table->msc_count); + "\n MPAM_INFO: Number of MSC nodes : %4d", g_mpam_info_table->msc_count); val_print(INFO, - "\n MPAM_INFO: Number of RSRC groups : %d", g_mpam_rsrc_group_count); + "\n MPAM_INFO: Number of RSRC groups : %4d", g_mpam_rsrc_group_count); val_print(DEBUG, "\n Memory mapping MSC nodes"); memory_map_msc(); @@ -1591,6 +1592,272 @@ val_mpam_get_max_intpartid(uint32_t msc_index) return BITFIELD_READ(INTPARTID_MAX, val_mpam_mmr_read(msc_index, REG_MPAMF_PARTID_NRW_IDR)); } +/** + @brief Selects a request or internal PARTID for subsequent configuration accesses. + @param msc_index - index of the MSC node in the MPAM info table. + @param partid - request or internal PARTID to select. + @param internal - true to select an internal PARTID, false for a request PARTID. +**/ +void +val_mpam_select_partid(uint32_t msc_index, uint16_t partid, bool internal) +{ + uint32_t data = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + + data = BITFIELD_WRITE(data, PART_SEL_PARTID_SEL, partid); + data = BITFIELD_WRITE(data, PART_SEL_INTERNAL, internal); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, data); +} + +/** + @brief Selects the control configuration for a request PARTID. + @param msc_index - index of the MSC node in the MPAM info table. + @param req_partid - request PARTID whose controls are configured. +**/ +static void +mpam_select_control_partid(uint32_t msc_index, uint16_t req_partid) +{ + uint16_t int_partid; + + /* Controls use the mapped internal PARTID only when narrowing is implemented. */ + if (val_mpam_msc_supports_partid_nrw(msc_index)) { + int_partid = val_mpam_get_partid_mapping(msc_index, req_partid); + val_mpam_select_partid(msc_index, int_partid, true); + } else { + val_mpam_select_partid(msc_index, req_partid, false); + } +} + +/** + @brief Gets the internal PARTID mapped to a request PARTID. + @param msc_index - index of the MSC node in the MPAM info table. + @param req_partid - request PARTID whose mapping is read. + @return Mapped internal PARTID. +**/ +uint16_t +val_mpam_get_partid_mapping(uint32_t msc_index, uint16_t req_partid) +{ + uint32_t data; + + val_mpam_select_partid(msc_index, req_partid, false); + data = val_mpam_mmr_read(msc_index, REG_MPAMCFG_INTPARTID); + return BITFIELD_READ(MPAMCFG_INTPARTID_INTPARTID, data); +} + +/** + @brief Maps a request PARTID to an internal PARTID. + @param msc_index - index of the MSC node in the MPAM info table. + @param req_partid - request PARTID to map. + @param int_partid - target internal PARTID. +**/ +void +val_mpam_set_partid_mapping(uint32_t msc_index, uint16_t req_partid, uint16_t int_partid) +{ + uint32_t data = 0; + + val_mpam_select_partid(msc_index, req_partid, false); + data = BITFIELD_WRITE(data, MPAMCFG_INTPARTID_INTPARTID, int_partid); + data = BITFIELD_WRITE(data, MPAMCFG_INTPARTID_INTERNAL, 1); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_INTPARTID, data); + val_mem_issue_dsb(); +} + +/** + @brief Gets the enable state for a request PARTID. + @param msc_index - index of the MSC node in the MPAM info table. + @param req_partid - request PARTID whose enable state is read. + @return true when the request PARTID is enabled or enable/disable is unsupported. +**/ +bool +val_mpam_msc_partid_is_enabled(uint32_t msc_index, uint16_t req_partid) +{ + uint32_t enabled_flags; + uint32_t saved_part_sel; + + if (!val_mpam_msc_supports_partid_endis(msc_index)) + return true; + + saved_part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + mpam_select_control_partid(msc_index, req_partid); + enabled_flags = val_mpam_mmr_read(msc_index, REG_MPAMCFG_EN_FLAGS); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + + return ((enabled_flags >> (req_partid & 0x1FU)) & 0x1U) != 0U; +} + +/** + @brief Prepares and enables a request PARTID for partition-control programming. + @param msc_index - index of the MSC node in the MPAM info table. + @param req_partid - request PARTID used to tag test traffic. + @param int_partid - internal PARTID used when narrowing is implemented. + @return ACS_STATUS_PASS on success, otherwise ACS_STATUS_ERR. +**/ +uint32_t +val_mpam_prepare_partid(uint32_t msc_index, uint16_t req_partid, uint16_t int_partid) +{ + /* Reject request PARTIDs outside the MSC request namespace. */ + if (req_partid > val_mpam_get_max_partid(msc_index)) + return ACS_STATUS_ERR; + + /* Establish and verify the request-to-internal mapping when required. */ + if (val_mpam_msc_supports_partid_nrw(msc_index)) { + if (int_partid > val_mpam_get_max_intpartid(msc_index)) + return ACS_STATUS_ERR; + + val_mpam_set_partid_mapping(msc_index, req_partid, int_partid); + if (val_mpam_get_partid_mapping(msc_index, req_partid) != int_partid) + return ACS_STATUS_ERR; + } + + /* Enable each request PARTID independently when enable/disable is implemented. */ + if (val_mpam_msc_supports_partid_endis(msc_index) && + val_mpam_msc_endis_partid(msc_index, 1, 0, req_partid)) + return ACS_STATUS_ERR; + + return ACS_STATUS_PASS; +} + +/** + @brief Reads an internal PARTID's CPBM configuration. + @param msc_index - index of the MSC node in the MPAM info table. + @param int_partid - internal PARTID whose CPBM is read. + @param cpbm - output buffer for CPBM words. + @param words - number of CPBM words to read. +**/ +void +val_mpam_read_cpbm(uint32_t msc_index, uint16_t int_partid, uint32_t *cpbm, uint32_t words) +{ + uint32_t max_words; + uint32_t word; + + max_words = (val_mpam_get_cpbm_width(msc_index) + 31U) / 32U; + if ((cpbm == NULL) || (words == 0U) || + (words > max_words) || (words > MAX_CPBM_WORDS)) + return; + + val_mpam_select_partid(msc_index, int_partid, true); + for (word = 0; word < words; word++) + cpbm[word] = val_mpam_mmr_read(msc_index, + REG_MPAMCFG_CPBM + (word * sizeof(uint32_t))); +} + +/** + @brief Writes an internal PARTID's CPBM configuration. + @param msc_index - index of the MSC node in the MPAM info table. + @param int_partid - internal PARTID whose CPBM is written. + @param cpbm - input buffer containing CPBM words. + @param words - number of CPBM words to write. +**/ +void +val_mpam_write_cpbm(uint32_t msc_index, uint16_t int_partid, + const uint32_t *cpbm, uint32_t words) +{ + uint32_t max_words; + uint32_t word; + + max_words = (val_mpam_get_cpbm_width(msc_index) + 31U) / 32U; + if ((cpbm == NULL) || (words == 0U) || + (words > max_words) || (words > MAX_CPBM_WORDS)) + return; + + val_mpam_select_partid(msc_index, int_partid, true); + for (word = 0; word < words; word++) + val_mpam_mmr_write(msc_index, REG_MPAMCFG_CPBM + (word * sizeof(uint32_t)), + cpbm[word]); + val_mem_issue_dsb(); +} + +/** + @brief Saves cache partition controls for a request PARTID. + @param msc_index - index of the MSC node in the MPAM info table. + @param req_partid - request PARTID whose effective controls are saved. + @param state - destination for the saved partition-control state. + @return true when all implemented controls were saved, otherwise false. +**/ +bool +val_mpam_save_partition_state(uint32_t msc_index, uint16_t req_partid, + MPAM_PARTITION_STATE *state) +{ + uint32_t cpbm_width; + uint32_t word; + + if (state == NULL) + return false; + + val_memory_set(state, sizeof(*state), 0); + state->selector = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + + /* Select and remember the effective request or internal control PARTID. */ + mpam_select_control_partid(msc_index, req_partid); + state->control_selector = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + + if (val_mpam_supports_cpor(msc_index)) { + cpbm_width = val_mpam_get_cpbm_width(msc_index); + state->cpbm_words = (cpbm_width + 31U) / 32U; + if ((cpbm_width == 0U) || (state->cpbm_words > MAX_CPBM_WORDS)) { + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, state->selector); + return false; + } + + state->cpbm = val_memory_alloc(state->cpbm_words * sizeof(uint32_t)); + if (state->cpbm == NULL) { + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, state->selector); + return false; + } + for (word = 0; word < state->cpbm_words; word++) + state->cpbm[word] = val_mpam_mmr_read( + msc_index, REG_MPAMCFG_CPBM + (word * sizeof(uint32_t))); + state->cpbm_valid = true; + } + + if (val_mpam_supports_ccap(msc_index)) { + state->cmax = val_mpam_mmr_read(msc_index, REG_MPAMCFG_CMAX); + state->cmax_valid = true; + } + + if (val_mpam_supports_cassoc(msc_index)) { + state->cassoc = val_mpam_mmr_read(msc_index, REG_MPAMCFG_CASSOC); + state->cassoc_valid = true; + } + + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, state->selector); + return true; +} + +/** + @brief Restores cache partition controls saved for a request PARTID. + @param msc_index - index of the MSC node in the MPAM info table. + @param state - previously saved state; its allocated CPBM storage is released. +**/ +void +val_mpam_restore_partition_state(uint32_t msc_index, + MPAM_PARTITION_STATE *state) +{ + uint32_t word; + + if (state == NULL) + return; + + /* Use the saved selector so restoration is independent of mapping changes. */ + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, state->control_selector); + + if (state->cpbm_valid) { + for (word = 0; word < state->cpbm_words; word++) + val_mpam_mmr_write(msc_index, + REG_MPAMCFG_CPBM + (word * sizeof(uint32_t)), + state->cpbm[word]); + val_memory_free(state->cpbm); + state->cpbm = NULL; + state->cpbm_valid = false; + } + if (state->cmax_valid) + val_mpam_mmr_write(msc_index, REG_MPAMCFG_CMAX, state->cmax); + if (state->cassoc_valid) + val_mpam_mmr_write(msc_index, REG_MPAMCFG_CASSOC, state->cassoc); + + val_mem_issue_dsb(); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, state->selector); +} + /** @brief This API gets number of fractional bits implemented in CCAP control. Prerequisite - If MSC supports RIS, Resource instance should be @@ -1710,17 +1977,12 @@ void val_mpam_configure_cpor(uint32_t msc_index, uint16_t partid, uint32_t cpbm_percentage) { uint32_t num_cpbm_bits; - uint32_t data; /* Get CPBM width */ num_cpbm_bits = val_mpam_get_cpbm_width(msc_index); - /* retaining other configured fields e.g, RIS index if supported */ - data = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); - - /* Select PARTID */ - data = BITFIELD_WRITE(data, PART_SEL_PARTID_SEL, partid); - val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, data); + /* Select the request PARTID or its mapped internal control PARTID. */ + mpam_select_control_partid(msc_index, partid); mpam_program_bitmap(msc_index, REG_MPAMCFG_CPBM, num_cpbm_bits, MAX_CPBM_WIDTH, cpbm_percentage); @@ -1731,6 +1993,26 @@ val_mpam_configure_cpor(uint32_t msc_index, uint16_t partid, uint32_t cpbm_perce return; } +/** + @brief Configures CPOR settings for an internal PARTID. + Prerequisite - The MSC implements PARTID narrowing and CPOR. + - If the MSC supports RIS, select the resource instance first. + @param msc_index - index of the MSC node in the MPAM info table. + @param int_partid - internal PARTID to configure. + @param cpbm_percentage - percentage of cache portions to assign. +**/ +void +val_mpam_configure_intpartid_cpor(uint32_t msc_index, uint16_t int_partid, + uint32_t cpbm_percentage) +{ + uint32_t num_cpbm_bits = val_mpam_get_cpbm_width(msc_index); + + val_mpam_select_partid(msc_index, int_partid, true); + mpam_program_bitmap(msc_index, REG_MPAMCFG_CPBM, num_cpbm_bits, + MAX_CPBM_WIDTH, cpbm_percentage); + val_mem_issue_dsb(); +} + /** @brief This API Configures CCAP settings for given MSC Prerequisite - If MSC supports RIS, Resource instance should be @@ -1748,16 +2030,12 @@ void val_mpam_configure_ccap(uint32_t msc_index, uint16_t partid, uint8_t num_fractional_bits; uint16_t fixed_point_fraction; - uint32_t data; num_fractional_bits = val_mpam_get_cmax_wd(msc_index); fixed_point_fraction = mpam_encode_fraction(num_fractional_bits, ccap_percentage, 1); - /* Select the PARTID to configure capacity partition parameters */ - data = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); - - data = BITFIELD_WRITE(data, PART_SEL_PARTID_SEL, partid); - val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, data); + /* Select the request PARTID or its mapped internal control PARTID. */ + mpam_select_control_partid(msc_index, partid); /* * Configure the CMAX register for the max capacity. @@ -1771,6 +2049,32 @@ void val_mpam_configure_ccap(uint32_t msc_index, uint16_t partid, return; } +/** + @brief Configures CCAP settings for an internal PARTID. + Prerequisite - The MSC implements PARTID narrowing and CCAP. + - If the MSC supports RIS, select the resource instance first. + @param msc_index - index of the MSC node in the MPAM info table. + @param int_partid - internal PARTID to configure. + @param softlim - enable or disable soft limiting for CCAP. + @param ccap_percentage - percentage of cache capacity to assign. +**/ +void +val_mpam_configure_intpartid_ccap(uint32_t msc_index, uint16_t int_partid, + uint8_t softlim, uint32_t ccap_percentage) +{ + uint8_t num_fractional_bits; + uint16_t fixed_point_fraction; + + num_fractional_bits = val_mpam_get_cmax_wd(msc_index); + fixed_point_fraction = mpam_encode_fraction(num_fractional_bits, ccap_percentage, 1); + + val_mpam_select_partid(msc_index, int_partid, true); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_CMAX, + ((uint32_t)softlim << MPAMCFG_CMAX_SOFTLIM_SHIFT) | + fixed_point_fraction); + val_mem_issue_dsb(); +} + /** @brief This API Configures CASSOC settings for given MSC and PARTID Prerequisite - If MSC supports RIS, Resource instance should be @@ -1787,15 +2091,12 @@ void val_mpam_configure_cassoc(uint32_t msc_index, uint16_t partid, uint8_t num_fractional_bits; uint16_t fixed_point_fraction; - uint32_t data; num_fractional_bits = val_mpam_get_cassoc_wd(msc_index); fixed_point_fraction = mpam_encode_fraction(num_fractional_bits, cassoc_percentage, 1); - /* Select the PARTID to configure CASSOC partition parameters */ - data = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); - data = BITFIELD_WRITE(data, PART_SEL_PARTID_SEL, partid); - val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, data); + /* Select the request PARTID or its mapped internal control PARTID. */ + mpam_select_control_partid(msc_index, partid); /* * Configure the CASSOC register for the cache associativity control settings. @@ -1823,15 +2124,12 @@ void val_mpam_configure_cmin(uint32_t msc_index, uint16_t partid, uint32_t cmin_ uint8_t num_fractional_bits; uint16_t fixed_point_fraction; - uint32_t data; num_fractional_bits = val_mpam_get_cmax_wd(msc_index); fixed_point_fraction = mpam_encode_fraction(num_fractional_bits, cmin_percentage, 0); - /* Select the PARTID to configure capacity partition parameters */ - data = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); - data = BITFIELD_WRITE(data, PART_SEL_PARTID_SEL, partid); - val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, data); + /* Select the request PARTID or its mapped internal control PARTID. */ + mpam_select_control_partid(msc_index, partid); /* * Configure the CMIN register - min cache capacity to be allocated for the PARTID. @@ -1859,14 +2157,11 @@ val_mpam_configure_mbwpbm(uint32_t msc_index, uint16_t partid, uint32_t mbwpbm_p { uint32_t num_mbwpbm_bits; - uint32_t data; num_mbwpbm_bits = val_mpam_get_mbwpbm_width(msc_index); - /* Select the PARTID to configure portion partition parameters */ - data = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); - data = BITFIELD_WRITE(data, PART_SEL_PARTID_SEL, partid); - val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, data); + /* Select the request PARTID or its mapped internal control PARTID. */ + mpam_select_control_partid(msc_index, partid); mpam_program_bitmap(msc_index, REG_MPAMCFG_MBW_PBM, num_mbwpbm_bits, MAX_BWPBM_WIDTH, mbwpbm_percentage); @@ -1888,15 +2183,12 @@ void val_mpam_msc_configure_mbwmin(uint32_t msc_index, uint16_t partid, uint32_t { uint8_t num_fractional_bits; uint16_t fixed_point_fraction; - uint32_t data; num_fractional_bits = val_mpam_get_bwa_wd(msc_index); fixed_point_fraction = mpam_encode_fraction(num_fractional_bits, mbwmin_percentage, 0); - /* Select the PARTID to configure minimum bandwidth limit parameters */ - data = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); - data = BITFIELD_WRITE(data, PART_SEL_PARTID_SEL, partid); - val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, data); + /* Select the request PARTID or its mapped internal control PARTID. */ + mpam_select_control_partid(msc_index, partid); /* * Configure the MBW_MIN register for minimum bandwidth limit. @@ -1925,15 +2217,12 @@ void val_mpam_msc_configure_mbwmax(uint32_t msc_index, uint16_t partid, { uint8_t num_fractional_bits; uint16_t fixed_point_fraction; - uint32_t data; num_fractional_bits = val_mpam_get_bwa_wd(msc_index); fixed_point_fraction = mpam_encode_fraction(num_fractional_bits, mbwmax_percentage, 1); - /* Select the PARTID to configure maximum bandwidth partition parameters */ - data = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); - data = BITFIELD_WRITE(data, PART_SEL_PARTID_SEL, partid); - val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, data); + /* Select the request PARTID or its mapped internal control PARTID. */ + mpam_select_control_partid(msc_index, partid); /* * Configure the MBW_MAX register for maximum bandwidth limit. @@ -2024,6 +2313,66 @@ val_mpam_configure_csu_mon(uint32_t msc_index, uint16_t partid, uint8_t pmg, uin return; } +/** + @brief Save the complete programmable state of a CSU monitor instance. + + @param msc_index - index of the MSC node in the MPAM info table. + @param mon_sel - CSU monitor instance to preserve. + @param state - storage for the saved monitor state. + @return None +**/ +void +val_mpam_save_csumon_state(uint32_t msc_index, uint16_t mon_sel, + MPAM_CSUMON_STATE *state) +{ + uint32_t csumon_idr; + + if (state == NULL) + return; + + /* Preserve the resource selection while selecting the monitor instance. */ + state->selector = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + state->selected_monitor = BITFIELD_WRITE(state->selector, MON_SEL_MON_SEL, mon_sel); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, state->selected_monitor); + + state->filter = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_CSU_FLT); + state->control = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_CSU_CTL); + state->value = BITFIELD_READ(MSMON_CSU_VALUE, + val_mpam_mmr_read(msc_index, REG_MSMON_CSU)); + csumon_idr = val_mpam_mmr_read(msc_index, REG_MPAMF_CSUMON_IDR); + state->value_writable = !BITFIELD_READ(CSUMON_IDR_CSU_RO, csumon_idr); + + /* Leave the caller's original monitor selection unchanged. */ + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, state->selector); +} + +/** + @brief Restore state saved by val_mpam_save_csumon_state(). + + @param msc_index - index of the MSC node in the MPAM info table. + @param state - saved monitor state to restore. + @return None +**/ +void +val_mpam_restore_csumon_state(uint32_t msc_index, const MPAM_CSUMON_STATE *state) +{ + uint32_t control; + + if (state == NULL) + return; + + /* Disable updates while restoring the monitor's filter and VALUE. */ + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, state->selected_monitor); + control = BITFIELD_WRITE(state->control, CSU_CTL_EN, 0); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_CSU_CTL, control); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_CSU_FLT, state->filter); + if (state->value_writable) + val_mpam_mmr_write(msc_index, REG_MSMON_CSU, state->value); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_CSU_CTL, state->control); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, state->selector); + val_mem_issue_dsb(); +} + /** @brief This API enables the Cache storage usage monitor. Prerequisite - If MSC supports RIS, Resource instance should be @@ -2527,6 +2876,8 @@ uint32_t val_mpam_program_el2(uint16_t partid, uint8_t pmg) uint32_t val_mpam_msc_endis_partid(uint32_t msc_index, bool enable, bool nfu_flag, uint16_t partid) { + uint32_t saved_part_sel; + uint16_t int_partid; /* Check if the PARTID is valid */ if (partid > val_mpam_get_max_partid(msc_index)) { @@ -2536,12 +2887,26 @@ val_mpam_msc_endis_partid(uint32_t msc_index, bool enable, bool nfu_flag, uint16 return 1; } + saved_part_sel = val_mpam_mmr_read(msc_index, REG_MPAMCFG_PART_SEL); + if (val_mpam_msc_supports_partid_nrw(msc_index)) { + int_partid = val_mpam_get_partid_mapping(msc_index, partid); + if (int_partid > val_mpam_get_max_intpartid(msc_index)) { + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); + return 1; + } + val_mpam_select_partid(msc_index, int_partid, true); + } else { + val_mpam_select_partid(msc_index, partid, false); + } + if (enable == 1) val_mpam_mmr_write(msc_index, REG_MPAMCFG_EN, BITFIELD_SET(MPAMCFG_EN_PARTID, partid)); else val_mpam_mmr_write(msc_index, REG_MPAMCFG_DIS, BITFIELD_SET(MPAMCFG_DIS_NFU, nfu_flag) | BITFIELD_SET(MPAMCFG_DIS_PARTID, partid)); + val_mem_issue_dsb(); + val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, saved_part_sel); val_mem_issue_dsb(); return 0; } diff --git a/val/src/acs_msc_error.c b/val/src/acs_msc_error.c index 6e5edfd6..e18b4893 100644 --- a/val/src/acs_msc_error.c +++ b/val/src/acs_msc_error.c @@ -105,7 +105,8 @@ uint32_t val_mpam_msc_generate_psr_error(uint32_t msc_index) if (max_partid == MPAMIDR_PARTID_MAX_MASK) { val_print(WARN, - "\n MSC index %d: Max PARTID is 0xFFFF; cannot test out-of-range PARTID. Skipping MSC", + "\n MSC index %d: Max PARTID is 0xFFFF; " + "cannot test out-of-range PARTID. Skipping MSC", msc_index); return ACS_STATUS_SKIP; } @@ -115,6 +116,8 @@ uint32_t val_mpam_msc_generate_psr_error(uint32_t msc_index) part_sel_value = (part_sel_value & ~(PART_SEL_PARTID_SEL_MASK << PART_SEL_PARTID_SEL_SHIFT)) | BITFIELD_SET(PART_SEL_PARTID_SEL, (max_partid + 1)); + /* Exercise the request PARTID range even when narrowing is implemented. */ + part_sel_value = BITFIELD_WRITE(part_sel_value, PART_SEL_INTERNAL, 0); val_mpam_mmr_write(msc_index, REG_MPAMCFG_PART_SEL, part_sel_value); val_print(DEBUG, "\n PARTID written to MPAMCFG_PART_SEL is %d", (max_partid + 1)); @@ -284,34 +287,48 @@ uint32_t val_mpam_msc_generate_pmgor_error(uint32_t msc_index) 1. Caller - Error Tests 2. Prerequisite - None @param msc_index - index of the MSC node in the MPAM info table. - @return None + @return ACS_STATUS_PASS if the error stimulus was generated, otherwise ACS_STATUS_SKIP. **/ -void val_mpam_msc_generate_msmon_config_error(uint32_t msc_index, uint16_t mon_count) +uint32_t val_mpam_msc_generate_msmon_config_error(uint32_t msc_index, uint16_t mon_count) { uint16_t max_partid; + uint32_t mon_sel; /* Extract max PARTID supported by this MSC */ max_partid = val_mpam_get_max_partid(msc_index); val_print(DEBUG, "\n MSC Max PARTID is %d", max_partid); - /* Write (mon_count-1) to MON_SEL register to configure the last monitor */ - val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, mon_count - 1); + /* A full-width PARTID range has no value available for this error stimulus. */ + if (max_partid == MPAMIDR_PARTID_MAX_MASK) { + val_print(WARN, + "\n MSC index %d: Max PARTID is 0xFFFF; " + "cannot generate monitor PARTID range error", + msc_index); + return ACS_STATUS_SKIP; + } + + /* Select the last valid monitor while preserving the current RIS. */ + mon_sel = val_mpam_mmr_read(msc_index, REG_MSMON_CFG_MON_SEL); + mon_sel = BITFIELD_WRITE(mon_sel, MON_SEL_MON_SEL, (mon_count - 1)); + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MON_SEL, mon_sel); if (val_mpam_supports_csumon(msc_index)) { /* Write (max_partid+1) to CSU monitor filter reg to raise monitor config error */ - val_mpam_mmr_write(msc_index, REG_MSMON_CFG_CSU_FLT, max_partid + 1); - return; + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_CSU_FLT, + BITFIELD_SET(CSU_FLT_PARTID, (max_partid + 1))); + return ACS_STATUS_PASS; } if (val_mpam_msc_supports_mbwumon(msc_index)) { /* Write (max_partid+1) to MBW monitor filter reg to raise monitor config error */ - val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MBWU_FLT, max_partid + 1); - return; + val_mpam_mmr_write(msc_index, REG_MSMON_CFG_MBWU_FLT, + BITFIELD_SET(MBWU_FLT_PARTID, (max_partid + 1))); + return ACS_STATUS_PASS; } val_mem_issue_dsb(); - return; + return ACS_STATUS_SKIP; } /** diff --git a/val/src/mpam_execute_test.c b/val/src/mpam_execute_test.c index ddc15aa2..bfc70da3 100644 --- a/val/src/mpam_execute_test.c +++ b/val/src/mpam_execute_test.c @@ -221,6 +221,8 @@ val_mpam_execute_cache_tests(uint32_t num_pe) status |= partition004_entry(num_pe); status |= partition005_entry(num_pe); status |= partition006_entry(num_pe); + status |= partition007_entry(num_pe); + status |= partition008_entry(num_pe); status |= feat001_entry(num_pe); diff --git a/val/src/rule_metadata.c b/val/src/rule_metadata.c index c1c9b301..2128b86f 100644 --- a/val/src/rule_metadata.c +++ b/val/src/rule_metadata.c @@ -2524,6 +2524,14 @@ rule_test_map_t rule_test_map[RULE_ID_SENTINEL] = { .flag = BASE_RULE, .test_num = ACS_MPAM_CACHE_TEST_NUM_BASE + 25, }, + [CSLHH] = { + .test_entry_id = PARTITION008_ENTRY, + .module_id = MPAM_CACHE, + .rule_desc = "Check narrowed request PARTID disable", + .platform_bitmask = PLATFORM_UEFI | PLATFORM_BAREMETAL, + .flag = BASE_RULE, + .test_num = ACS_MPAM_CACHE_TEST_NUM_BASE + 8, + }, [VFJMZ] = { .test_entry_id = FEAT001_ENTRY, .module_id = MPAM_CACHE, @@ -2692,13 +2700,21 @@ rule_test_map_t rule_test_map[RULE_ID_SENTINEL] = { .flag = BASE_RULE, .test_num = ACS_MPAM_MEMORY_TEST_NUM_BASE + 3, }, + [GFYDF] = { + .test_entry_id = PARTITION007_ENTRY, + .module_id = MPAM_CACHE, + .rule_desc = "Check request to internal PARTID mapping", + .platform_bitmask = PLATFORM_UEFI | PLATFORM_BAREMETAL, + .flag = BASE_RULE, + .test_num = ACS_MPAM_CACHE_TEST_NUM_BASE + 7, + }, [ZGGMX] = { - .test_entry_id = MONITOR001_ENTRY, + .test_entry_id = PARTITION007_ENTRY, .module_id = MPAM_CACHE, - .rule_desc = "Check PMG Storage by CPOR Nodes", + .rule_desc = "Check request PARTID monitor filtering", .platform_bitmask = PLATFORM_UEFI | PLATFORM_BAREMETAL, .flag = BASE_RULE, - .test_num = ACS_MPAM_MONITOR_TEST_NUM_BASE + 1, + .test_num = ACS_MPAM_CACHE_TEST_NUM_BASE + 7, }, [YFMQP] = { .test_entry_id = MEM001_ENTRY, @@ -2871,7 +2887,7 @@ rule_test_map_t rule_test_map[RULE_ID_SENTINEL] = { [ZWRQW] = { .test_entry_id = ERROR003_ENTRY, .module_id = MPAM_ERROR, - .rule_desc = "Check MSMON ConfigID out-of-range Err", + .rule_desc = "Check MPAMCFG write with out-of-range PARTID", .platform_bitmask = PLATFORM_UEFI | PLATFORM_BAREMETAL, .flag = BASE_RULE, .test_num = ACS_MPAM_ERROR_TEST_NUM_BASE + 3, @@ -4345,6 +4361,8 @@ test_entry_fn_t test_entry_func_table[TEST_ENTRY_SENTINEL] = { [PARTITION002_ENTRY] = partition002_entry, [PARTITION005_ENTRY] = partition005_entry, [PARTITION006_ENTRY] = partition006_entry, + [PARTITION007_ENTRY] = partition007_entry, + [PARTITION008_ENTRY] = partition008_entry, [MEM002_ENTRY] = mem002_entry, [MEM003_ENTRY] = mem003_entry, [MONITOR007_ENTRY] = monitor007_entry, @@ -4770,6 +4788,8 @@ test_entry_fn_t test_entry_func_table[TEST_ENTRY_SENTINEL] = { [PARTITION002_ENTRY] = partition002_entry, [PARTITION005_ENTRY] = partition005_entry, [PARTITION006_ENTRY] = partition006_entry, + [PARTITION007_ENTRY] = partition007_entry, + [PARTITION008_ENTRY] = partition008_entry, [MEM002_ENTRY] = mem002_entry, [MEM003_ENTRY] = mem003_entry, [MONITOR007_ENTRY] = monitor007_entry,