xref: /xnu-10063.101.15/osfmk/mach/exclaves_l4.h (revision 94d3b452840153a99b38a3a9659680b2a006908e)
1 /*
2  * Copyright (c) 2022 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #ifndef _MACH_EXCLAVES_L4_H
30 #define _MACH_EXCLAVES_L4_H
31 
32 #include <mach/exclaves.h>
33 
34 /*
35  * Equivalent versions of the subset of cL4 APIs needed for construction of
36  * IPC message buffers & tags (with prefix changed L4_ -> Exclaves_L4_)
37  */
38 
39 __BEGIN_DECLS
40 
41 #ifdef PRIVATE
42 
43 #if defined(__LP64__)
44 
45 /* -------------------------------------------------------------------------- */
46 
47 /* Void data type */
48 typedef void Exclaves_L4_Void_t;
49 
50 /** Boolean data type */;
51 typedef unsigned char Exclaves_L4_Bool_t;
52 
53 /** Unsigned 32-bit data type */
54 typedef unsigned int Exclaves_L4_Word32_t;
55 
56 /** Unsigned data type with native word length size */
57 typedef unsigned long Exclaves_L4_Word_t;
58 
59 /** Data type for L4 system call return values */
60 typedef Exclaves_L4_Word_t Exclaves_L4_Error_t;
61 
62 /**
63  * Convert a value into a native word type
64  *
65  * @param x Value to convert
66  * @return Value cast to the native word type
67  */
68 #define Exclaves_L4_Word(x) ((Exclaves_L4_Word_t) (x))
69 
70 /**
71  * Convert a value into a 32-bit word type
72  *
73  * @param x Value to convert
74  * @return Value cast to the 32-bit word type
75  */
76 #define Exclaves_L4_Word32(x) ((Exclaves_L4_Word32_t) (x))
77 
78 #define __Exclaves_L4_Packed __attribute__ ((__packed__))
79 
80 /* Size of native word length in bits */
81 #define Exclaves_L4_WordBits (64)
82 
83 /**
84  * Produce a bitfield mask
85  *
86  * @param base Starting bit position within the created mask
87  * @param bits Number of bits to set in the mask
88  * @return A mask value
89  */
90 #define Exclaves_L4_BfmW(base, bits) \
91     (((~Exclaves_L4_Word(0)) >> (Exclaves_L4_WordBits - (bits))) << (base))
92 
93 /**
94  * Extract a value from a bitfield
95  *
96  * @param bitfield Bitfield contents to extract a value from
97  * @param base Starting bit position of the value to extract
98  * @param bits Number of bits for the value within the bitfield
99  * @return The extracted value
100  */
101 #define Exclaves_L4_BfxW(bitfield, base, bits) \
102     (((bitfield) & Exclaves_L4_BfmW((base), (bits))) >> (base))
103 
104 /**
105  * Return a bitfield with a particular value inserted
106  *
107  * @param bitfield Bitfield current contents
108  * @param base Starting bit position of the value to insert
109  * @param bits Number of bits for the value within the bitfield
110  * @param value Value to insert
111  * @return The bitfield with the value inserted, overwriting any previous
112  * value in the corresponding bits
113  */
114 #define Exclaves_L4_BfiW(bitfield, base, bits, value) \
115     (((bitfield) & (~Exclaves_L4_BfmW((base), (bits)))) | \
116 	(((Exclaves_L4_Word(value)) << (base)) & Exclaves_L4_BfmW((base), (bits))))
117 
118 /**
119  * Return an otherwise empty bitfield that had a value inserted
120  *
121  * @param base Starting bit position of the value to insert
122  * @param bits Number of bits for the value within the bitfield
123  * @param value Value to insert
124  * @return The value encoded in the bitfield, with all other bits set to 0.
125  */
126 #define Exclaves_L4_BfW(base, bits, value) \
127     (Exclaves_L4_BfiW(Exclaves_L4_Word(0), (base), (bits), (value)))
128 
129 /** Nil (zero) value */
130 #define Exclaves_L4_Nil ((Exclaves_L4_Word_t) 0)
131 
132 /** Boolean true value */
133 #define Exclaves_L4_True ((Exclaves_L4_Bool_t) 1)
134 
135 /** Boolean false value */
136 #define Exclaves_L4_False ((Exclaves_L4_Bool_t) 0)
137 
138 /* -------------------------------------------------------------------------- */
139 
140 /**
141  * Create an Exclaves_L4_Error from a provided code and value
142  *
143  * @param code Specific Exclaves_L4_Error code
144  * @param value Supplementary information providing more context about
145  * the error
146  * @return The complete error value
147  */
148 #define Exclaves_L4_Error(code, value) (((Exclaves_L4_Word(value)) << 8) | (Exclaves_L4_Word(code)))
149 
150 /**
151  * Extract the error code from a constructed Exclaves_L4_Error_t
152  *
153  * @param error The error
154  * @return The error code
155  */
156 #define Exclaves_L4_ErrorCode(error) ((error) & (((Exclaves_L4_Word(1)) << 8) - 1))
157 
158 /**
159  * Extract the error value from a constructed Exclaves_L4_Error_t
160  *
161  * @param error The error
162  * @return The error value
163  */
164 #define Exclaves_L4_ErrorValue(error) ((error) >> 8)
165 
166 /**
167  * L4 error codes
168  */
169 
170 enum {
171 	Exclaves_L4_ErrorCodeSuccess,
172 	Exclaves_L4_ErrorCodePreempted,
173 	Exclaves_L4_ErrorCodeCanceled,
174 	Exclaves_L4_ErrorCodeTruncated,
175 	Exclaves_L4_ErrorCodeCapInvalid,
176 	Exclaves_L4_ErrorCodeSlotInvalid,
177 	Exclaves_L4_ErrorCodeMethodInvalid,
178 	Exclaves_L4_ErrorCodeArgumentInvalid,
179 	Exclaves_L4_ErrorCodeOperationInvalid,
180 	Exclaves_L4_ErrorCodePermissionInvalid,
181 	Exclaves_L4_ErrorCodeMax
182 };
183 
184 #define Exclaves_L4_Success \
185     Exclaves_L4_Error(Exclaves_L4_ErrorCodeSuccess, Exclaves_L4_Nil)
186 #define Exclaves_L4_Preempted \
187     Exclaves_L4_Error(Exclaves_L4_ErrorCodePreempted, Exclaves_L4_Nil)
188 #define Exclaves_L4_ErrorCanceled(reason) \
189     Exclaves_L4_Error(Exclaves_L4_ErrorCodeCanceled, reason)
190 #define Exclaves_L4_ErrorTruncated \
191     Exclaves_L4_Error(Exclaves_L4_ErrorCodeTruncated, Exclaves_L4_Nil)
192 #define Exclaves_L4_ErrorCapInvalid \
193     Exclaves_L4_Error(Exclaves_L4_ErrorCodeCapInvalid, Exclaves_L4_Nil)
194 #define Exclaves_L4_ErrorSlotInvalid \
195     Exclaves_L4_Error(Exclaves_L4_ErrorCodeSlotInvalid, Exclaves_L4_Nil)
196 #define Exclaves_L4_ErrorMethodInvalid \
197     Exclaves_L4_Error(Exclaves_L4_ErrorCodeMethodInvalid, Exclaves_L4_Nil)
198 #define Exclaves_L4_ErrorArgumentInvalid(argument) \
199     Exclaves_L4_Error(Exclaves_L4_ErrorCodeArgumentInvalid, argument)
200 #define Exclaves_L4_ErrorOperationInvalid \
201     Exclaves_L4_Error(Exclaves_L4_ErrorCodeOperationInvalid, Exclaves_L4_Nil)
202 #define Exclaves_L4_ErrorPermissionInvalid \
203     Exclaves_L4_Error(Exclaves_L4_ErrorCodePermissionInvalid, Exclaves_L4_Nil)
204 
205 /* -------------------------------------------------------------------------- */
206 
207 /* Exclaves_L4_MessageTag_t
208  *
209  *  32                             0
210  *  llllllllllllllll...nuuucccrrrrrr
211  *
212  * r: message registers (6 bits)
213  * c: capability registers (3 bits)
214  * u: unwrapped capabilities (3 bits)
215  * n: non-blocking (1 bit)
216  * l: label (16 bits)
217  */
218 
219 typedef Exclaves_L4_Word_t Exclaves_L4_MessageTag_t;
220 
221 /* Exclaves_L4_MessageTag_Mrs */
222 #define Exclaves_L4_MessageTag_Mrs_Base 0
223 #define Exclaves_L4_MessageTag_Mrs_Bits 6
224 
225 /* Exclaves_L4_MessageTag_Crs */
226 #define Exclaves_L4_MessageTag_Crs_Base 6
227 #define Exclaves_L4_MessageTag_Crs_Bits 3
228 
229 /* Exclaves_L4_MessageTag_Unwrapped */
230 #define Exclaves_L4_MessageTag_Unwrapped_Base 9
231 #define Exclaves_L4_MessageTag_Unwrapped_Bits 3
232 
233 /* Exclaves_L4_MessageTag_NonBlocking */
234 #define Exclaves_L4_MessageTag_NonBlocking_Base 12
235 #define Exclaves_L4_MessageTag_NonBlocking_Bits 1
236 
237 /* Exclaves_L4_MessageTag_Label */
238 #define Exclaves_L4_MessageTag_Label_Base 16
239 #define Exclaves_L4_MessageTag_Label_Bits 16
240 
241 static inline Exclaves_L4_Word_t
Exclaves_L4_MessageTag_Mrs(Exclaves_L4_MessageTag_t tag)242 Exclaves_L4_MessageTag_Mrs(Exclaves_L4_MessageTag_t tag)
243 {
244 	return Exclaves_L4_Word(Exclaves_L4_BfxW(tag, Exclaves_L4_MessageTag_Mrs_Base,
245 	           Exclaves_L4_MessageTag_Mrs_Bits));
246 }
247 
248 static inline Exclaves_L4_Word_t
Exclaves_L4_MessageTag_Crs(Exclaves_L4_MessageTag_t tag)249 Exclaves_L4_MessageTag_Crs(Exclaves_L4_MessageTag_t tag)
250 {
251 	return Exclaves_L4_Word(Exclaves_L4_BfxW(tag, Exclaves_L4_MessageTag_Crs_Base,
252 	           Exclaves_L4_MessageTag_Crs_Bits));
253 }
254 
255 static inline Exclaves_L4_Word_t
Exclaves_L4_MessageTag_Unwrapped(Exclaves_L4_MessageTag_t tag)256 Exclaves_L4_MessageTag_Unwrapped(Exclaves_L4_MessageTag_t tag)
257 {
258 	return Exclaves_L4_Word(Exclaves_L4_BfxW(tag, Exclaves_L4_MessageTag_Unwrapped_Base,
259 	           Exclaves_L4_MessageTag_Unwrapped_Bits));
260 }
261 
262 static inline Exclaves_L4_Word_t
Exclaves_L4_MessageTag_Label(Exclaves_L4_MessageTag_t tag)263 Exclaves_L4_MessageTag_Label(Exclaves_L4_MessageTag_t tag)
264 {
265 	return Exclaves_L4_Word(Exclaves_L4_BfxW(tag, Exclaves_L4_MessageTag_Label_Base,
266 	           Exclaves_L4_MessageTag_Label_Bits));
267 }
268 
269 static inline Exclaves_L4_MessageTag_t
Exclaves_L4_MessageTag(Exclaves_L4_Word_t mrs,Exclaves_L4_Word_t crs,Exclaves_L4_Word_t label,Exclaves_L4_Bool_t nonblocking)270 Exclaves_L4_MessageTag(Exclaves_L4_Word_t mrs, Exclaves_L4_Word_t crs, Exclaves_L4_Word_t label,
271     Exclaves_L4_Bool_t nonblocking)
272 {
273 	Exclaves_L4_Word_t tag = (
274 		Exclaves_L4_BfW(Exclaves_L4_MessageTag_Mrs_Base,
275 		Exclaves_L4_MessageTag_Mrs_Bits, Exclaves_L4_Word(mrs)) |
276 		Exclaves_L4_BfW(Exclaves_L4_MessageTag_Crs_Base,
277 		Exclaves_L4_MessageTag_Crs_Bits, Exclaves_L4_Word(crs)) |
278 		Exclaves_L4_BfW(Exclaves_L4_MessageTag_NonBlocking_Base,
279 		Exclaves_L4_MessageTag_NonBlocking_Bits, Exclaves_L4_Word(nonblocking)) |
280 		Exclaves_L4_BfW(Exclaves_L4_MessageTag_Label_Base,
281 		Exclaves_L4_MessageTag_Label_Bits, Exclaves_L4_Word(label)));
282 
283 	return (Exclaves_L4_MessageTag_t) tag;
284 }
285 
286 /* -------------------------------------------------------------------------- */
287 
288 /* Exclaves_L4_IpcBuffer_t  */
289 
290 /* number of ipc buffer message registers */
291 #define Exclaves_L4_IpcBuffer_Mrs 56
292 /* numver of ipc buffer capability registers */
293 #define Exclaves_L4_IpcBuffer_Crs 4
294 /* ipc buffer size */
295 #define Exclaves_L4_IpcBuffer_Size (sizeof(Exclaves_L4_IpcBuffer_t))
296 
297 /* ipc buffer object */
298 typedef struct __Exclaves_L4_Packed {
299 	/* message registers */
300 	Exclaves_L4_Word_t mr[Exclaves_L4_IpcBuffer_Mrs];
301 	/* source capability registers */
302 	Exclaves_L4_Word_t scr[Exclaves_L4_IpcBuffer_Crs];
303 	/* destination capability registers */
304 	Exclaves_L4_Word_t dcr[Exclaves_L4_IpcBuffer_Crs];
305 } Exclaves_L4_IpcBuffer_t;
306 
307 /** Cast to a Exclaves IPC buffer pointer */
308 #define Exclaves_L4_IpcBuffer_Ptr(x) \
309 	(__unsafe_forge_single(Exclaves_L4_IpcBuffer_t *, (x)))
310 
311 /* L4 IPC invocation message registers */
312 enum {
313 	Exclaves_L4_Ipc_Mr_Tag,
314 	Exclaves_L4_Ipc_Mr_Badge,
315 	Exclaves_L4_Ipc_Mr_Message
316 };
317 
318 #ifdef KERNEL_PRIVATE
319 
320 static inline Exclaves_L4_IpcBuffer_t *
Exclaves_L4_IpcBuffer(Exclaves_L4_Void_t)321 Exclaves_L4_IpcBuffer(Exclaves_L4_Void_t)
322 {
323 	return Exclaves_L4_IpcBuffer_Ptr(exclaves_get_ipc_buffer());
324 }
325 
326 #endif /* KERNEL_PRIVATE */
327 
328 #ifdef MACH_KERNEL_PRIVATE
329 
330 /* -------------------------------------------------------------------------- */
331 
332 static inline Exclaves_L4_Word_t
Exclaves_L4_GetMr(Exclaves_L4_Word32_t mr)333 Exclaves_L4_GetMr(Exclaves_L4_Word32_t mr)
334 {
335 	return Exclaves_L4_IpcBuffer()->mr[mr];
336 }
337 
338 static inline Exclaves_L4_Void_t
Exclaves_L4_SetMr(Exclaves_L4_Word32_t mr,Exclaves_L4_Word_t word)339 Exclaves_L4_SetMr(Exclaves_L4_Word32_t mr, Exclaves_L4_Word_t word)
340 {
341 	Exclaves_L4_IpcBuffer()->mr[mr] = word;
342 }
343 
344 static inline Exclaves_L4_Void_t
Exclaves_L4_SetMrs(Exclaves_L4_Word32_t mr,Exclaves_L4_Word32_t count,Exclaves_L4_Word_t * __counted_by (count)words)345 Exclaves_L4_SetMrs(Exclaves_L4_Word32_t mr, Exclaves_L4_Word32_t count,
346     Exclaves_L4_Word_t * __counted_by(count)words)
347 {
348 	Exclaves_L4_IpcBuffer_t *ipcb = Exclaves_L4_IpcBuffer();
349 
350 	for (Exclaves_L4_Word32_t offset = 0; offset < count; offset++) {
351 		ipcb->mr[mr + offset] = words[offset];
352 	}
353 }
354 
355 static inline Exclaves_L4_Void_t
Exclaves_L4_GetMrs(Exclaves_L4_Word32_t mr,Exclaves_L4_Word32_t count,Exclaves_L4_Word_t * __counted_by (count)words)356 Exclaves_L4_GetMrs(Exclaves_L4_Word32_t mr, Exclaves_L4_Word32_t count,
357     Exclaves_L4_Word_t * __counted_by(count)words)
358 {
359 	Exclaves_L4_IpcBuffer_t *ipcb = Exclaves_L4_IpcBuffer();
360 
361 	for (Exclaves_L4_Word32_t offset = 0; offset < count; offset++) {
362 		words[offset] = ipcb->mr[mr + offset];
363 	}
364 }
365 
366 static inline Exclaves_L4_Word_t
Exclaves_L4_GetCr(Exclaves_L4_Word32_t cr,Exclaves_L4_Bool_t dst)367 Exclaves_L4_GetCr(Exclaves_L4_Word32_t cr, Exclaves_L4_Bool_t dst)
368 {
369 	if (dst == Exclaves_L4_True) {
370 		return Exclaves_L4_IpcBuffer()->dcr[cr];
371 	} else {
372 		return Exclaves_L4_IpcBuffer()->scr[cr];
373 	}
374 }
375 
376 static inline Exclaves_L4_Void_t
Exclaves_L4_SetCr(Exclaves_L4_Word32_t cr,Exclaves_L4_Word_t word,Exclaves_L4_Bool_t dst)377 Exclaves_L4_SetCr(Exclaves_L4_Word32_t cr, Exclaves_L4_Word_t word, Exclaves_L4_Bool_t dst)
378 {
379 	if (dst == Exclaves_L4_True) {
380 		Exclaves_L4_IpcBuffer()->dcr[cr] = word;
381 	} else {
382 		Exclaves_L4_IpcBuffer()->scr[cr] = word;
383 	}
384 }
385 
386 static inline Exclaves_L4_MessageTag_t
Exclaves_L4_GetMessageTag(Exclaves_L4_Void_t)387 Exclaves_L4_GetMessageTag(Exclaves_L4_Void_t)
388 {
389 	return (Exclaves_L4_MessageTag_t) (Exclaves_L4_GetMr(Exclaves_L4_Ipc_Mr_Tag));
390 }
391 
392 static inline Exclaves_L4_Void_t
Exclaves_L4_SetMessageTag(Exclaves_L4_MessageTag_t tag)393 Exclaves_L4_SetMessageTag(Exclaves_L4_MessageTag_t tag)
394 {
395 	Exclaves_L4_SetMr(Exclaves_L4_Ipc_Mr_Tag, Exclaves_L4_Word(tag));
396 }
397 
398 static inline Exclaves_L4_Word_t
Exclaves_L4_GetMessageMr(Exclaves_L4_Word32_t mr)399 Exclaves_L4_GetMessageMr(Exclaves_L4_Word32_t mr)
400 {
401 	return Exclaves_L4_GetMr(Exclaves_L4_Ipc_Mr_Message + mr);
402 }
403 
404 static inline Exclaves_L4_Void_t
Exclaves_L4_SetMessageMr(Exclaves_L4_Word32_t mr,Exclaves_L4_Word_t word)405 Exclaves_L4_SetMessageMr(Exclaves_L4_Word32_t mr, Exclaves_L4_Word_t word)
406 {
407 	Exclaves_L4_SetMr((Exclaves_L4_Ipc_Mr_Message + mr), word);
408 }
409 
410 #endif /* MACH_KERNEL_PRIVATE */
411 
412 /* -------------------------------------------------------------------------- */
413 
414 /* Private communication protocol between Libsyscall, xnu and xnu proxy */
415 
416 /* Return value of the endpoint message forwarding call. */
417 #define EXCLAVES_XNU_PROXY_CR_RETVAL(ipcb) ((ipcb)->dcr[3])
418 
419 /* identifiers for exclaves reachable through xnuproxy */
420 typedef enum : uint64_t {
421 	/* HelloExclaves: c-hello-exclave */
422 	EXCLAVES_XNUPROXY_EXCLAVE_HELLOEXCLAVE = 0,
423 	/* templated user_app */
424 	EXCLAVES_XNUPROXY_EXCLAVE_USERAPP,
425 	/* HelloTightbeam: swift-hello-exclave */
426 	EXCLAVES_XNUPROXY_EXCLAVE_HELLOTIGHTBEAM,
427 	/* HelloDrivers */
428 	EXCLAVES_XNUPROXY_EXCLAVE_HELLODRIVERS,
429 	/* HelloStorage */
430 	EXCLAVES_XNUPROXY_EXCLAVE_HELLOSTORAGE,
431 	/* templated user_app2 */
432 	EXCLAVES_XNUPROXY_EXCLAVE_USERAPP2,
433 	/* templated user_app3 */
434 	EXCLAVES_XNUPROXY_EXCLAVE_USERAPP3,
435 	/* audio */
436 	EXCLAVES_XNUPROXY_EXCLAVE_AUDIODRIVER,
437 	/* HelloDriverInterrupts */
438 	EXCLAVES_XNUPROXY_EXCLAVE_HELLODRIVERINTERRUPTS,
439 	/* ExclaveDriverKit */
440 	EXCLAVES_XNUPROXY_EXCLAVE_EXCLAVEDRIVERKIT,
441 	/* SecureRTBuddy for AOP */
442 	EXCLAVES_XNUPROXY_EXCLAVE_SECURERTBUDDY_AOP,
443 	/* SecureRTBuddy for DCP */
444 	EXCLAVES_XNUPROXY_EXCLAVE_SECURERTBUDDY_DCP,
445 	/* conclave launcher control */
446 	EXCLAVES_XNUPROXY_EXCLAVE_CONCLAVECONTROL,
447 	/* conclave launcher control */
448 	EXCLAVES_XNUPROXY_EXCLAVE_CONCLAVEDEBUG,
449 	/* SecureRTBuddy EDK connection for AOP */
450 	EXCLAVES_XNUPROXY_EXCLAVE_SECURERTBUDDY_AOP_EDK,
451 	/* SecureRTBuddy EDK connection for DCP */
452 	EXCLAVES_XNUPROXY_EXCLAVE_SECURERTBUDDY_DCP_EDK,
453 } exclaves_xnuproxy_exclaves_t;
454 
455 typedef enum : uint32_t {
456 	EXCLAVES_XNUPROXY_TEST_BUF1 = 1,
457 	EXCLAVES_XNUPROXY_TEST_BUF2,
458 	EXCLAVES_XNUPROXY_TEST_BUF3,
459 	EXCLAVES_XNUPROXY_TEST_BUF4,
460 	/* 5 is empty */
461 	EXCLAVES_XNUPROXY_NAMED_BUFFER_STORAGE_BUF_1 = 6,
462 	EXCLAVES_XNUPROXY_NAMED_BUFFER_STORAGE_BUF_2 = 7,
463 
464 	EXCLAVES_XNUPROXY_LAST_STATIC_BUF = 47,
465 	EXCLAVES_XNUPROXY_TEST_DYN_BUF1,
466 	EXCLAVES_XNUPROXY_TEST_DYN_BUF2,
467 } exclaves_named_buffer_id_t;
468 
469 #endif /* defined(__LP64__) */
470 
471 #endif /* PRIVATE */
472 
473 __END_DECLS
474 
475 #endif /* _MACH_EXCLAVES_L4_H */
476