Skip to content

Commit 7078246

Browse files
committed
[CHERIoT] Add cheriot_mmio, cheriot_shared_object and cheriot_cap_import attributes
The first two are used from the front-end to indicate that an extern global declaration refers to a specific compartment import. The third is the attribute consumed by the back-end to generate the necessary compartment import entry and the correct sequence of instructions when taking the address of the global.
1 parent 7df08ed commit 7078246

23 files changed

+1300
-65
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2892,6 +2892,19 @@ def CHERILibCall : DeclOrTypeAttr {
28922892
let Subjects = SubjectList<[Function], ErrorDiag>;
28932893
}
28942894

2895+
def CHERIOTMMIODevice : DeclOrTypeAttr {
2896+
let Spellings = [GNU<"cheriot_mmio">];
2897+
let Documentation = [CHERIOTMMIODeviceDocs];
2898+
let Args = [StringArgument<"DeviceName">, StringArgument<"EncodedPermissions", 1>];
2899+
let Subjects = SubjectList<[GlobalVar], ErrorDiag>;
2900+
}
2901+
2902+
def CHERIOTSharedObject : DeclOrTypeAttr {
2903+
let Spellings = [GNU<"cheriot_shared_object">];
2904+
let Documentation = [CHERIOTSharedObjectDocs];
2905+
let Args = [StringArgument<"ObjectName">, StringArgument<"EncodedPermissions", 1>];
2906+
let Subjects = SubjectList<[GlobalVar], ErrorDiag>;
2907+
}
28952908

28962909
def CHERINoSubobjectBounds : DeclOrTypeAttr {
28972910
let Spellings = [GNU<"cheri_no_subobject_bounds">,

clang/include/clang/Basic/AttrDocs.td

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,6 +1473,73 @@ Further reading for other annotations:
14731473
}];
14741474
}
14751475

1476+
def CHERIOTMMIODeviceDocs : Documentation {
1477+
let Category = DocCatVariable;
1478+
let Content = [{
1479+
Indicates that the global it refers to must be treated as a cross-compartment
1480+
object. In particular, this attribute indicates that the object is related to
1481+
an MMIO-bound device. The target global must have the `extern` and `volatile`
1482+
qualifiers.
1483+
1484+
**Usage**: ``__attribute((cheriot_mmio("<device>",
1485+
"<permissions_encoding>")))``, where ``"<device>"`` is the (quoted) identifier
1486+
of the MMIO-bound device. An example of this is ``"uart"``. The
1487+
``"<permissions_encoding>"`` indicates the permissions of the capability
1488+
resulting from the import. The permissions encoding is a string of variable
1489+
length. The symbols allowed in the string are ``R``, ``W``, ``c`` and ``m``,
1490+
whose meaning is explained in the table below.
1491+
1492+
1493+
.. csv-table:: Supported Syntaxes
1494+
:header: "Symbol", "Description", "Meaning", "Depends on"
1495+
1496+
"``R``","Load (Read)", "May be used to read.",""
1497+
"``W``","Store (Write)", "May be used to write.",""
1498+
"``c``","Load / Store Capability", "May be used to load or store capabilities as well as non-capability data.","``R`` or ``W``"
1499+
"``m``","Load Mutable", "May be used to load capabilities with write permission.","``R``"
1500+
1501+
Examples of valid encodings are: ``"RWcm"`` (all permissions), ``"R"`` (read
1502+
only), etc. Note that the order in which symbols appear is not relevant: for
1503+
example, `"RWcm"` and `"mcWR"` are both valid and entail the same permissions.
1504+
1505+
**Warning**: The ``<permissions_encoding>`` parameter is optional, and if no encoding is given ``"RWcm"`` is assumed.
1506+
}];
1507+
}
1508+
1509+
1510+
def CHERIOTSharedObjectDocs : Documentation {
1511+
let Category = DocCatVariable;
1512+
let Content = [{
1513+
Indicates that the global it refers to must be treated as a cross-compartment
1514+
object. In particular, this attribute indicates that the object is a
1515+
cross-compartment shared object. The target global must have the `extern` and
1516+
`volatile` qualifiers.
1517+
1518+
1519+
**Usage**: ``__attribute((cheriot_shared_object("<object>",
1520+
"<permissions_encoding>")))``, where ``"<object>"`` is the (quoted) identifier
1521+
of the shared object. The ``"<permissions_encoding>"`` indicates the permissions
1522+
of the capability resulting from the import. The permissions encoding is a
1523+
string of variable length. The symbols allowed in the string are ``R``, ``W``,
1524+
``c`` and ``m``, whose meaning is explained in the table below.
1525+
1526+
1527+
.. csv-table:: Supported Syntaxes
1528+
:header: "Symbol", "Description", "Meaning", "Depends on"
1529+
1530+
"``R``","Load (Read)", "May be used to read.",""
1531+
"``W``","Store (Write)", "May be used to write.",""
1532+
"``c``","Load / Store Capability", "May be used to load or store capabilities as well as non-capability data.","``R`` or ``W``"
1533+
"``m``","Load Mutable", "May be used to load capabilities with write permission.","``R``"
1534+
1535+
Examples of valid encodings are: ``"RWcm"`` (all permissions), ``"R"`` (read
1536+
only), etc. Note that the order in which symbols appear is not relevant: for
1537+
example, `"RWcm"` and `"mcWR"` are both valid and entail the same permissions.
1538+
1539+
**Warning**: The ``<permissions_encoding>`` parameter is optional, and if no encoding is given ``"RWcm"`` is assumed.
1540+
}];
1541+
}
1542+
14761543
def ObjCMethodFamilyDocs : Documentation {
14771544
let Category = DocCatFunction;
14781545
let Content = [{

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,16 @@ def err_half_const_requires_fp16 : Error<
140140
"half precision constant requires cl_khr_fp16">;
141141
def err_cheriot_minstack_without_annotation : Error<
142142
"__cheriot_minimum_stack__ can only be used in a function with a minimum stack annotation">;
143+
def err_cheriot_malformed_cap_permissions_unknown_sym : Error<
144+
"the permissions in %0 contain unknown permission symbols: '%1' (value: '%2') ">;
145+
def err_cheriot_malformed_cap_permissions_invalid_dep : Error<
146+
"the permissions in %0 contain ill-formed dependencies: %1 (value: '%2') ">;
147+
def warn_cheriot_cap_permissions_duplicate_sym : Warning<
148+
"the permissions in %0 contain a duplicate permission symbol: %1 (value: '%2') ">;
149+
def warn_cheriot_global_cap_import_non_volatile : Warning<
150+
"global variable definition '%0' has attribute %1 but is not qualified as `volatile`">;
151+
def err_cheriot_global_cap_import_initialized: Error<
152+
"global variable definition '%0' with attribute %1 cannot have an initializer">;
143153

144154
// C99 variable-length arrays
145155
def ext_vla : Extension<"variable length arrays are a C99 feature">,

clang/lib/CodeGen/Targets/RISCV.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "ABIInfoImpl.h"
10-
#include "TargetInfo.h"
1110
#include "CommonCheriTargetCodeGenInfo.h"
11+
#include "TargetInfo.h"
1212

1313
using namespace clang;
1414
using namespace clang::CodeGen;
@@ -632,6 +632,33 @@ class RISCVTargetCodeGenInfo : public CommonCheriTargetCodeGenInfo {
632632

633633
void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
634634
CodeGen::CodeGenModule &CGM) const override {
635+
if (const auto *VD = dyn_cast_or_null<VarDecl>(D)) {
636+
if (auto *GVar = llvm::dyn_cast<llvm::GlobalVariable>(GV)) {
637+
638+
// If the clang declaration was annotated with `cheriot_mmio(...)`, we
639+
// need to propagate the attribute by translating it to a matching
640+
// `cheriot_cap_import` attribute in the generated LLVM IR, so that the
641+
// back-end can generate the proper import entries and correctly
642+
// translate references to this global.
643+
if (VD->hasAttr<CHERIOTMMIODeviceAttr>()) {
644+
auto *Attr = D->getAttr<CHERIOTMMIODeviceAttr>();
645+
llvm::CHERIoTGlobalCapabilityImportAttr CapImportAttr(
646+
llvm::CHERIoTGlobalCapabilityImportAttr::MMIO,
647+
Attr->getDeviceName(), Attr->getEncodedPermissions());
648+
GVar->addAttribute(CapImportAttr.getAttrName(), CapImportAttr.str());
649+
}
650+
651+
// Same for the `cheriot_shared_object(...)` attribute.
652+
if (VD->hasAttr<CHERIOTSharedObjectAttr>()) {
653+
auto *Attr = D->getAttr<CHERIOTSharedObjectAttr>();
654+
llvm::CHERIoTGlobalCapabilityImportAttr CapImportAttr(
655+
llvm::CHERIoTGlobalCapabilityImportAttr::SharedObject,
656+
Attr->getObjectName(), Attr->getEncodedPermissions());
657+
GVar->addAttribute(CapImportAttr.getAttrName(), CapImportAttr.str());
658+
}
659+
}
660+
}
661+
635662
const auto *FD = dyn_cast_or_null<FunctionDecl>(D);
636663
if (!FD) return;
637664

clang/lib/Sema/SemaDecl.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13501,6 +13501,26 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
1350113501
return;
1350213502
}
1350313503

13504+
// CHERIoT-specific check: if the decl has the `cheriot_mmio` or
13505+
// `cheriot_shared_object` attributes and also has an explicit definition, an
13506+
// error must be generated.
13507+
if (RealDecl->hasAttr<CHERIOTSharedObjectAttr>() ||
13508+
RealDecl->hasAttr<CHERIOTMMIODeviceAttr>()) {
13509+
const IdentifierInfo *AttrName;
13510+
13511+
if (auto *Attr = RealDecl->getAttr<CHERIOTSharedObjectAttr>())
13512+
AttrName = Attr->getAttrName();
13513+
else {
13514+
AttrName = RealDecl->getAttr<CHERIOTMMIODeviceAttr>()->getAttrName();
13515+
}
13516+
13517+
Diag(RealDecl->getLocation(),
13518+
diag::err_cheriot_global_cap_import_initialized)
13519+
<< VDecl->getName() << AttrName;
13520+
VDecl->setInvalidDecl();
13521+
return;
13522+
}
13523+
1350413524
// WebAssembly tables can't be used to initialise a variable.
1350513525
if (!Init->getType().isNull() && Init->getType()->isWebAssemblyTableType()) {
1350613526
Diag(Init->getExprLoc(), diag::err_wasm_table_art) << 0;

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828
#include "clang/Basic/DarwinSDKInfo.h"
2929
#include "clang/Basic/IdentifierTable.h"
3030
#include "clang/Basic/LangOptions.h"
31+
#include "clang/Basic/Linkage.h"
3132
#include "clang/Basic/SourceLocation.h"
3233
#include "clang/Basic/SourceManager.h"
34+
#include "clang/Basic/Specifiers.h"
3335
#include "clang/Basic/TargetInfo.h"
3436
#include "clang/Lex/Preprocessor.h"
3537
#include "clang/Sema/Attr.h"
@@ -61,6 +63,7 @@
6163
#include "llvm/ADT/STLForwardCompat.h"
6264
#include "llvm/ADT/StringExtras.h"
6365
#include "llvm/Demangle/Demangle.h"
66+
#include "llvm/IR/Attributes.h"
6467
#include "llvm/IR/DerivedTypes.h"
6568
#include "llvm/MC/MCSectionMachO.h"
6669
#include "llvm/Support/Error.h"
@@ -2103,6 +2106,127 @@ static void handleCHERIMethodSuffix(Sema &S, Decl *D, const ParsedAttr &Attr) {
21032106
D->addAttr(::new (S.Context) CHERIMethodSuffixAttr(S.Context, Attr, Str));
21042107
}
21052108

2109+
static void handleCHERIOTMMIODevice(Sema &S, Decl *D, const ParsedAttr &Attr,
2110+
Sema::DeclAttributeLocation DAL) {
2111+
auto *VD = dyn_cast<VarDecl>(D);
2112+
if (!VD || !VD->hasGlobalStorage())
2113+
return;
2114+
2115+
if (!VD->hasExternalStorage()) {
2116+
VD->setStorageClass(clang::SC_Extern);
2117+
}
2118+
2119+
QualType QT = VD->getType();
2120+
if (!QT.isVolatileQualified()) {
2121+
S.Diag(Attr.getLoc(), diag::warn_cheriot_global_cap_import_non_volatile)
2122+
<< VD->getName() << Attr.getAttrName();
2123+
}
2124+
2125+
StringRef DeviceName;
2126+
SourceLocation DeviceNameLiteralLoc;
2127+
if (!S.checkStringLiteralArgumentAttr(Attr, 0, DeviceName,
2128+
&DeviceNameLiteralLoc))
2129+
return;
2130+
StringRef Permissions;
2131+
SourceLocation PermissionsLiteralLoc;
2132+
2133+
if (Attr.getNumArgs() > 1) {
2134+
S.checkStringLiteralArgumentAttr(Attr, 1, Permissions,
2135+
&PermissionsLiteralLoc);
2136+
}
2137+
2138+
std::string OwnedPermissions = Permissions.str();
2139+
2140+
auto DuplicateSymbolCallback = [&Attr, &S,
2141+
Permissions](char DuplicateSymbol) {
2142+
S.Diag(Attr.getLoc(), diag::warn_cheriot_cap_permissions_duplicate_sym)
2143+
<< Attr.getAttrName() << "'" + std::string({DuplicateSymbol}) + "'"
2144+
<< Permissions;
2145+
};
2146+
2147+
auto ExtraneousSymbolCallback = [&Attr, &S,
2148+
Permissions](StringRef ExtraneousSymbols) {
2149+
S.Diag(Attr.getLoc(),
2150+
diag::err_cheriot_malformed_cap_permissions_unknown_sym)
2151+
<< Attr.getAttrName() << ExtraneousSymbols << Permissions;
2152+
};
2153+
2154+
auto FailedSemanticCheckCallback = [&Attr, &S,
2155+
Permissions](StringRef Reason) {
2156+
S.Diag(Attr.getLoc(),
2157+
diag::err_cheriot_malformed_cap_permissions_invalid_dep)
2158+
<< Attr.getAttrName() << Reason << Permissions;
2159+
};
2160+
2161+
if (!llvm::CHERIoTGlobalCapabilityImportAttr::checkPermissions(
2162+
OwnedPermissions, DuplicateSymbolCallback, ExtraneousSymbolCallback,
2163+
FailedSemanticCheckCallback))
2164+
return;
2165+
2166+
return D->addAttr(::new (S.Context) CHERIOTMMIODeviceAttr(
2167+
S.Context, Attr, DeviceName, OwnedPermissions));
2168+
}
2169+
2170+
static void handleCHERIOTSharedObject(Sema &S, Decl *D, const ParsedAttr &Attr,
2171+
Sema::DeclAttributeLocation DAL) {
2172+
auto *VD = dyn_cast<VarDecl>(D);
2173+
if (!VD || !VD->hasGlobalStorage())
2174+
return;
2175+
2176+
if (VD->hasInit()) {
2177+
S.Diag(Attr.getLoc(), diag::err_cheriot_global_cap_import_initialized)
2178+
<< VD->getName() << Attr.getAttrName();
2179+
}
2180+
2181+
if (!VD->hasExternalStorage()) {
2182+
VD->setStorageClass(clang::SC_Extern);
2183+
}
2184+
2185+
StringRef ObjectName;
2186+
SourceLocation ObjectNameLiteralLoc;
2187+
if (!S.checkStringLiteralArgumentAttr(Attr, 0, ObjectName,
2188+
&ObjectNameLiteralLoc))
2189+
return;
2190+
StringRef Permissions;
2191+
SourceLocation PermissionsLiteralLoc;
2192+
2193+
if (Attr.getNumArgs() > 1) {
2194+
S.checkStringLiteralArgumentAttr(Attr, 1, Permissions,
2195+
&PermissionsLiteralLoc);
2196+
}
2197+
2198+
std::string OwnedPermissions = Permissions.str();
2199+
2200+
auto DuplicateSymbolCallback = [&Attr, &S,
2201+
Permissions](char DuplicateSymbol) {
2202+
S.Diag(Attr.getLoc(), diag::warn_cheriot_cap_permissions_duplicate_sym)
2203+
<< Attr.getAttrName() << "'" + std::string({DuplicateSymbol}) + "'"
2204+
<< Permissions;
2205+
};
2206+
2207+
auto ExtraneousSymbolCallback = [&Attr, &S,
2208+
Permissions](StringRef ExtraneousSymbols) {
2209+
S.Diag(Attr.getLoc(),
2210+
diag::err_cheriot_malformed_cap_permissions_unknown_sym)
2211+
<< Attr.getAttrName() << ExtraneousSymbols << Permissions;
2212+
};
2213+
2214+
auto FailedSemanticCheckCallback = [&Attr, &S,
2215+
Permissions](StringRef Reason) {
2216+
S.Diag(Attr.getLoc(),
2217+
diag::err_cheriot_malformed_cap_permissions_invalid_dep)
2218+
<< Attr.getAttrName() << Reason << Permissions;
2219+
};
2220+
2221+
if (!llvm::CHERIoTGlobalCapabilityImportAttr::checkPermissions(
2222+
OwnedPermissions, DuplicateSymbolCallback, ExtraneousSymbolCallback,
2223+
FailedSemanticCheckCallback))
2224+
return;
2225+
2226+
D->addAttr(::new (S.Context) CHERIOTSharedObjectAttr(
2227+
S.Context, Attr, ObjectName, OwnedPermissions));
2228+
}
2229+
21062230
static void handleCHERICompartmentName(Sema &S, Decl *D, const ParsedAttr &Attr,
21072231
Sema::DeclAttributeLocation DAL) {
21082232
// cheri_compartment is both:
@@ -7350,6 +7474,12 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
73507474
case ParsedAttr::AT_CHERICompartmentName:
73517475
handleCHERICompartmentName(S, D, AL, DAL);
73527476
break;
7477+
case ParsedAttr::AT_CHERIOTMMIODevice:
7478+
handleCHERIOTMMIODevice(S, D, AL, DAL);
7479+
break;
7480+
case ParsedAttr::AT_CHERIOTSharedObject:
7481+
handleCHERIOTSharedObject(S, D, AL, DAL);
7482+
break;
73537483
case ParsedAttr::AT_InterruptState:
73547484
handleInterruptState(S, D, AL);
73557485
break;

0 commit comments

Comments
 (0)