1ebc8e1ffSMarouene Boubakri/* SPDX-License-Identifier: (BSD-2-Clause AND MIT) */ 2ebc8e1ffSMarouene Boubakri/* 3ebc8e1ffSMarouene Boubakri * Copyright 2022-2023 NXP 4ebc8e1ffSMarouene Boubakri */ 5ebc8e1ffSMarouene Boubakri 6ebc8e1ffSMarouene Boubakri/* 7ebc8e1ffSMarouene Boubakri * Copyright (c) 2014, Linaro Limited 8ebc8e1ffSMarouene Boubakri * All rights reserved. 9ebc8e1ffSMarouene Boubakri * 10ebc8e1ffSMarouene Boubakri * Redistribution and use in source and binary forms, with or without 11ebc8e1ffSMarouene Boubakri * modification, are permitted provided that the following conditions are met: 12ebc8e1ffSMarouene Boubakri * 13ebc8e1ffSMarouene Boubakri * 1. Redistributions of source code must retain the above copyright notice, 14ebc8e1ffSMarouene Boubakri * this list of conditions and the following disclaimer. 15ebc8e1ffSMarouene Boubakri * 16ebc8e1ffSMarouene Boubakri * 2. Redistributions in binary form must reproduce the above copyright notice, 17ebc8e1ffSMarouene Boubakri * this list of conditions and the following disclaimer in the documentation 18ebc8e1ffSMarouene Boubakri * and/or other materials provided with the distribution. 19ebc8e1ffSMarouene Boubakri * 20ebc8e1ffSMarouene Boubakri * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21ebc8e1ffSMarouene Boubakri * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22ebc8e1ffSMarouene Boubakri * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23ebc8e1ffSMarouene Boubakri * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 24ebc8e1ffSMarouene Boubakri * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25ebc8e1ffSMarouene Boubakri * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26ebc8e1ffSMarouene Boubakri * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27ebc8e1ffSMarouene Boubakri * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28ebc8e1ffSMarouene Boubakri * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29ebc8e1ffSMarouene Boubakri * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30ebc8e1ffSMarouene Boubakri * POSSIBILITY OF SUCH DAMAGE. 31ebc8e1ffSMarouene Boubakri */ 32ebc8e1ffSMarouene Boubakri 33ebc8e1ffSMarouene Boubakri/* 34ebc8e1ffSMarouene Boubakri * Copyright (c) 2008-2010 Travis Geiselbrecht 35ebc8e1ffSMarouene Boubakri * 36ebc8e1ffSMarouene Boubakri * Permission is hereby granted, free of charge, to any person obtaining 37ebc8e1ffSMarouene Boubakri * a copy of this software and associated documentation files 38ebc8e1ffSMarouene Boubakri * (the "Software"), to deal in the Software without restriction, 39ebc8e1ffSMarouene Boubakri * including without limitation the rights to use, copy, modify, merge, 40ebc8e1ffSMarouene Boubakri * publish, distribute, sublicense, and/or sell copies of the Software, 41ebc8e1ffSMarouene Boubakri * and to permit persons to whom the Software is furnished to do so, 42ebc8e1ffSMarouene Boubakri * subject to the following conditions: 43ebc8e1ffSMarouene Boubakri * 44ebc8e1ffSMarouene Boubakri * The above copyright notice and this permission notice shall be 45ebc8e1ffSMarouene Boubakri * included in all copies or substantial portions of the Software. 46ebc8e1ffSMarouene Boubakri * 47ebc8e1ffSMarouene Boubakri * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 48ebc8e1ffSMarouene Boubakri * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 49ebc8e1ffSMarouene Boubakri * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 50ebc8e1ffSMarouene Boubakri * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 51ebc8e1ffSMarouene Boubakri * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 52ebc8e1ffSMarouene Boubakri * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 53ebc8e1ffSMarouene Boubakri * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 54ebc8e1ffSMarouene Boubakri */ 55ebc8e1ffSMarouene Boubakri 56ebc8e1ffSMarouene Boubakri#include <mm/core_mmu.h> 57ebc8e1ffSMarouene Boubakri#include <platform_config.h> 58ebc8e1ffSMarouene Boubakri#include <util.h> 59ebc8e1ffSMarouene Boubakri 60ebc8e1ffSMarouene Boubakri/* 61ebc8e1ffSMarouene Boubakri * Note: 62ebc8e1ffSMarouene Boubakri * Clang 11 (ld.lld) generates non-relocatable reference when using ROUNDDOWN() 63ebc8e1ffSMarouene Boubakri * from <util.h>, which does not work with ASLR. 64ebc8e1ffSMarouene Boubakri */ 65ebc8e1ffSMarouene Boubakri#define LD_ROUNDDOWN(x, y) ((x) - ((x) % (y))) 66ebc8e1ffSMarouene Boubakri 67ebc8e1ffSMarouene BoubakriOUTPUT_FORMAT(CFG_KERN_LINKER_FORMAT) 68ebc8e1ffSMarouene BoubakriOUTPUT_ARCH(CFG_KERN_LINKER_ARCH) 69ebc8e1ffSMarouene Boubakri 70ebc8e1ffSMarouene BoubakriENTRY(_start) 71ebc8e1ffSMarouene BoubakriSECTIONS 72ebc8e1ffSMarouene Boubakri{ 73ee34e7eaSJens Wiklander . = TEE_LOAD_ADDR; 74ebc8e1ffSMarouene Boubakri /* Ensure text section is page aligned */ 75ee34e7eaSJens Wiklander ASSERT(!(TEE_LOAD_ADDR & (SMALL_PAGE_SIZE - 1)), 76ebc8e1ffSMarouene Boubakri "text start should align to 4Kb") 77ebc8e1ffSMarouene Boubakri 78ebc8e1ffSMarouene Boubakri __text_start = .; 79ebc8e1ffSMarouene Boubakri 80ebc8e1ffSMarouene Boubakri /* 81ee34e7eaSJens Wiklander * Memory between TEE_LOAD_ADDR and page aligned rounded down 82ebc8e1ffSMarouene Boubakri * value will be mapped with unpaged "text" section attributes: 83ebc8e1ffSMarouene Boubakri * likely to be read-only/executable. 84ebc8e1ffSMarouene Boubakri */ 85ebc8e1ffSMarouene Boubakri __flatmap_rx_start = LD_ROUNDDOWN(__text_start, SMALL_PAGE_SIZE); 86ebc8e1ffSMarouene Boubakri 87ebc8e1ffSMarouene Boubakri .text : { 88ebc8e1ffSMarouene Boubakri KEEP(*(.text._start)) 89ebc8e1ffSMarouene Boubakri __identity_map_init_start = .; 90ebc8e1ffSMarouene Boubakri __text_data_start = .; 91ebc8e1ffSMarouene Boubakri *(.identity_map.data) 92ebc8e1ffSMarouene Boubakri __text_data_end = .; 93ebc8e1ffSMarouene Boubakri *(.identity_map .identity_map.*) 94ebc8e1ffSMarouene Boubakri __identity_map_init_end = .; 95ebc8e1ffSMarouene Boubakri *(.text .text.*) 96ebc8e1ffSMarouene Boubakri *(.sram.text.glue_7* .gnu.linkonce.t.*) 97ebc8e1ffSMarouene Boubakri . = ALIGN(8); 98ebc8e1ffSMarouene Boubakri } 99ebc8e1ffSMarouene Boubakri __text_end = .; 100ebc8e1ffSMarouene Boubakri 101ebc8e1ffSMarouene Boubakri#ifdef CFG_CORE_RODATA_NOEXEC 102ebc8e1ffSMarouene Boubakri . = ALIGN(SMALL_PAGE_SIZE); 103ebc8e1ffSMarouene Boubakri#endif 104ebc8e1ffSMarouene Boubakri __flatmap_rx_size = . - __flatmap_rx_start; 105ebc8e1ffSMarouene Boubakri __flatmap_ro_start = .; 106ebc8e1ffSMarouene Boubakri 107ebc8e1ffSMarouene Boubakri .rodata : ALIGN(8) { 108ebc8e1ffSMarouene Boubakri __rodata_start = .; 109ebc8e1ffSMarouene Boubakri *(.gnu.linkonce.r.*) 110ebc8e1ffSMarouene Boubakri *(.rodata .rodata.*) 111ebc8e1ffSMarouene Boubakri#ifndef CFG_CORE_ASLR 112ebc8e1ffSMarouene Boubakri . = ALIGN(8); 113ebc8e1ffSMarouene Boubakri KEEP(*(SORT(.scattered_array*))); 114ebc8e1ffSMarouene Boubakri#endif 115ebc8e1ffSMarouene Boubakri . = ALIGN(8); 116ebc8e1ffSMarouene Boubakri __rodata_end = .; 117ebc8e1ffSMarouene Boubakri } 118ebc8e1ffSMarouene Boubakri 119*911f059bSYu-Chien Peter Lin .dynsym : { 120*911f059bSYu-Chien Peter Lin __dyn_sym_start = .; 121*911f059bSYu-Chien Peter Lin *(.dynsym) 122*911f059bSYu-Chien Peter Lin __dyn_sym_end = .; 123*911f059bSYu-Chien Peter Lin } 124*911f059bSYu-Chien Peter Lin 125*911f059bSYu-Chien Peter Lin .rel.dyn : { 126*911f059bSYu-Chien Peter Lin *(.rel.*) 127*911f059bSYu-Chien Peter Lin } 128*911f059bSYu-Chien Peter Lin 129*911f059bSYu-Chien Peter Lin .rela.dyn : ALIGN(8) { 130*911f059bSYu-Chien Peter Lin PROVIDE(__rel_dyn_start = .); 131*911f059bSYu-Chien Peter Lin *(.rela*) 132*911f059bSYu-Chien Peter Lin PROVIDE(__rel_dyn_end = .); 133*911f059bSYu-Chien Peter Lin } 134*911f059bSYu-Chien Peter Lin 135*911f059bSYu-Chien Peter Lin#if defined(CFG_CORE_ASLR) 136*911f059bSYu-Chien Peter Lin .data.rel.ro : { 137*911f059bSYu-Chien Peter Lin . = ALIGN(8); 138*911f059bSYu-Chien Peter Lin KEEP(*(SORT(.scattered_array*))); 139*911f059bSYu-Chien Peter Lin *(.data.rel.ro.__unpaged .data.rel.ro.__unpaged.*) 140*911f059bSYu-Chien Peter Lin } 141*911f059bSYu-Chien Peter Lin#endif 142*911f059bSYu-Chien Peter Lin 143ebc8e1ffSMarouene Boubakri .got : { *(.got.plt) *(.got) } 144ebc8e1ffSMarouene Boubakri .note.gnu.property : { *(.note.gnu.property) } 145ebc8e1ffSMarouene Boubakri .plt : { *(.plt) } 146ebc8e1ffSMarouene Boubakri 147ebc8e1ffSMarouene Boubakri .ctors : ALIGN(8) { 148ebc8e1ffSMarouene Boubakri __ctor_list = .; 149ebc8e1ffSMarouene Boubakri KEEP(*(.ctors .ctors.* .init_array .init_array.*)) 150ebc8e1ffSMarouene Boubakri __ctor_end = .; 151ebc8e1ffSMarouene Boubakri } 152ebc8e1ffSMarouene Boubakri .dtors : ALIGN(8) { 153ebc8e1ffSMarouene Boubakri __dtor_list = .; 154ebc8e1ffSMarouene Boubakri KEEP(*(.dtors .dtors.* .fini_array .fini_array.*)) 155ebc8e1ffSMarouene Boubakri __dtor_end = .; 156ebc8e1ffSMarouene Boubakri } 157ebc8e1ffSMarouene Boubakri 158ebc8e1ffSMarouene Boubakri /* Start page aligned read-write memory */ 159ebc8e1ffSMarouene Boubakri#ifdef CFG_CORE_RWDATA_NOEXEC 160ebc8e1ffSMarouene Boubakri . = ALIGN(SMALL_PAGE_SIZE); 161ebc8e1ffSMarouene Boubakri#endif 162ebc8e1ffSMarouene Boubakri __flatmap_ro_size = . - __flatmap_ro_start; 163ebc8e1ffSMarouene Boubakri __flatmap_rw_start = .; 164ebc8e1ffSMarouene Boubakri 165ebc8e1ffSMarouene Boubakri .data : ALIGN(8) { 166ebc8e1ffSMarouene Boubakri /* writable data */ 167ebc8e1ffSMarouene Boubakri __data_start_rom = .; 168ebc8e1ffSMarouene Boubakri /* in one segment binaries, the rom data address is on top 169ebc8e1ffSMarouene Boubakri of the ram data address */ 170ebc8e1ffSMarouene Boubakri __data_start = .; 171ebc8e1ffSMarouene Boubakri *(.data .data.* .gnu.linkonce.d.*) 172ebc8e1ffSMarouene Boubakri . = ALIGN(8); 173ebc8e1ffSMarouene Boubakri /* 174ebc8e1ffSMarouene Boubakri * To allow the linker relax accesses to global symbols, 175ebc8e1ffSMarouene Boubakri * those need to be within imm12 (signed 12-bit) offsets 176ebc8e1ffSMarouene Boubakri * from __global_pointer$. 177ebc8e1ffSMarouene Boubakri */ 178ebc8e1ffSMarouene Boubakri PROVIDE(__global_pointer$ = . + 0x800 ); 179851d05e6SYu Chien Peter Lin *(.sdata .sdata.* .gnu.linkonce.s.*) 180ebc8e1ffSMarouene Boubakri } 181ebc8e1ffSMarouene Boubakri 182ebc8e1ffSMarouene Boubakri /* uninitialized data */ 183ebc8e1ffSMarouene Boubakri .bss : { 184ebc8e1ffSMarouene Boubakri __data_end = .; 185ebc8e1ffSMarouene Boubakri __bss_start = .; 186851d05e6SYu Chien Peter Lin *(.sbss .sbss.*) 187851d05e6SYu Chien Peter Lin *(.gnu.linkonce.sb.*) 188ebc8e1ffSMarouene Boubakri *(.bss .bss.*) 189ebc8e1ffSMarouene Boubakri *(.gnu.linkonce.b.*) 190ebc8e1ffSMarouene Boubakri *(COMMON) 191ebc8e1ffSMarouene Boubakri . = ALIGN(8); 192ebc8e1ffSMarouene Boubakri __bss_end = .; 193ebc8e1ffSMarouene Boubakri } 194ebc8e1ffSMarouene Boubakri 195ebc8e1ffSMarouene Boubakri .heap1 (NOLOAD) : { 196ebc8e1ffSMarouene Boubakri /* 197ebc8e1ffSMarouene Boubakri * We're keeping track of the padding added before the 198ebc8e1ffSMarouene Boubakri * .nozi section so we can do something useful with 199ebc8e1ffSMarouene Boubakri * this otherwise wasted memory. 200ebc8e1ffSMarouene Boubakri */ 201ebc8e1ffSMarouene Boubakri __heap1_start = .; 202ebc8e1ffSMarouene Boubakri . += CFG_CORE_HEAP_SIZE; 203ebc8e1ffSMarouene Boubakri . = ALIGN(4 * 1024); 204ebc8e1ffSMarouene Boubakri __heap1_end = .; 205ebc8e1ffSMarouene Boubakri } 206ebc8e1ffSMarouene Boubakri /* 207ebc8e1ffSMarouene Boubakri * Uninitialized data that shouldn't be zero initialized at 208ebc8e1ffSMarouene Boubakri * runtime. 209ebc8e1ffSMarouene Boubakri */ 210ebc8e1ffSMarouene Boubakri .nozi (NOLOAD) : { 211ebc8e1ffSMarouene Boubakri __nozi_start = .; 212ebc8e1ffSMarouene Boubakri KEEP(*(.nozi .nozi.*)) 213ebc8e1ffSMarouene Boubakri . = ALIGN(16); 214ebc8e1ffSMarouene Boubakri __nozi_end = .; 215ebc8e1ffSMarouene Boubakri __nozi_stack_start = .; 216ebc8e1ffSMarouene Boubakri KEEP(*(.nozi_stack .nozi_stack.*)) 217ebc8e1ffSMarouene Boubakri . = ALIGN(8); 218ebc8e1ffSMarouene Boubakri __nozi_stack_end = .; 219ebc8e1ffSMarouene Boubakri } 220ebc8e1ffSMarouene Boubakri 221a5ac48d6SJens Wiklander . = ALIGN(SMALL_PAGE_SIZE); 222a5ac48d6SJens Wiklander __flatmap_free_start = .; 223a5ac48d6SJens Wiklander __flatmap_rw_size = __flatmap_free_start - __flatmap_rw_start; 224a5ac48d6SJens Wiklander 225ebc8e1ffSMarouene Boubakri#ifdef CFG_CORE_SANITIZE_KADDRESS 226ee34e7eaSJens Wiklander . = TEE_RAM_START + (TEE_RAM_VA_SIZE * 8) / 9 - 8; 227ebc8e1ffSMarouene Boubakri . = ALIGN(8); 228ebc8e1ffSMarouene Boubakri .asan_shadow : { 229ebc8e1ffSMarouene Boubakri __asan_shadow_start = .; 230ebc8e1ffSMarouene Boubakri . += TEE_RAM_VA_SIZE / 9; 231ebc8e1ffSMarouene Boubakri __asan_shadow_end = .; 232ebc8e1ffSMarouene Boubakri __asan_shadow_size = __asan_shadow_end - __asan_shadow_start; 233ebc8e1ffSMarouene Boubakri } 234ebc8e1ffSMarouene Boubakri#endif /*CFG_CORE_SANITIZE_KADDRESS*/ 235ebc8e1ffSMarouene Boubakri 236ebc8e1ffSMarouene Boubakri __end = .; 237ee34e7eaSJens Wiklander __init_size = __data_end - TEE_LOAD_ADDR; 238ebc8e1ffSMarouene Boubakri 239ebc8e1ffSMarouene Boubakri /* 240ebc8e1ffSMarouene Boubakri * Guard against moving the location counter backwards in the assignment 241ebc8e1ffSMarouene Boubakri * below. 242ebc8e1ffSMarouene Boubakri */ 243ee34e7eaSJens Wiklander ASSERT(. <= (TEE_RAM_START + TEE_RAM_VA_SIZE), 244ebc8e1ffSMarouene Boubakri "TEE_RAM_VA_SIZE is too small") 245ee34e7eaSJens Wiklander . = TEE_RAM_START + TEE_RAM_VA_SIZE; 246ebc8e1ffSMarouene Boubakri 247ebc8e1ffSMarouene Boubakri _end_of_ram = .; 248ebc8e1ffSMarouene Boubakri 249ebc8e1ffSMarouene Boubakri __get_tee_init_end = .; 250a5ac48d6SJens Wiklander __flatmap_free_size = _end_of_ram - __flatmap_free_start; 251ebc8e1ffSMarouene Boubakri 252*911f059bSYu-Chien Peter Lin . = ALIGN(8); 253ebc8e1ffSMarouene Boubakri 254ebc8e1ffSMarouene Boubakri#ifndef CFG_CORE_ASLR 255*911f059bSYu-Chien Peter Lin ASSERT(SIZEOF(.rel.dyn) == 0, "Relocation entries not expected") 256*911f059bSYu-Chien Peter Lin ASSERT(SIZEOF(.rela.dyn) == 0, "Relocation entries not expected") 257ebc8e1ffSMarouene Boubakri#endif 258ebc8e1ffSMarouene Boubakri 259ebc8e1ffSMarouene Boubakri /DISCARD/ : { 260ebc8e1ffSMarouene Boubakri /* Strip unnecessary stuff */ 261*911f059bSYu-Chien Peter Lin *(.comment .note .eh_frame .interp .rela.plt) 262ebc8e1ffSMarouene Boubakri /* Strip meta variables */ 263ebc8e1ffSMarouene Boubakri *(__keep_meta_vars*) 264ebc8e1ffSMarouene Boubakri } 265ebc8e1ffSMarouene Boubakri 266ebc8e1ffSMarouene Boubakri} 267ebc8e1ffSMarouene Boubakri 268ebc8e1ffSMarouene Boubakri/* Unpaged read-only memories */ 269ebc8e1ffSMarouene Boubakri__vcore_unpg_rx_start = __flatmap_rx_start; 270ebc8e1ffSMarouene Boubakri__vcore_unpg_ro_start = __flatmap_ro_start; 271ebc8e1ffSMarouene Boubakri#ifdef CFG_CORE_RODATA_NOEXEC 272ebc8e1ffSMarouene Boubakri__vcore_unpg_rx_size = __flatmap_rx_size; 273ebc8e1ffSMarouene Boubakri__vcore_unpg_ro_size = __flatmap_ro_size; 274ebc8e1ffSMarouene Boubakri#else 275ebc8e1ffSMarouene Boubakri__vcore_unpg_rx_size = __flatmap_rx_size + __flatmap_ro_size; 276ebc8e1ffSMarouene Boubakri__vcore_unpg_ro_size = 0; 277ebc8e1ffSMarouene Boubakri#endif 278ebc8e1ffSMarouene Boubakri__vcore_unpg_rx_end = __vcore_unpg_rx_start + __vcore_unpg_rx_size; 279ebc8e1ffSMarouene Boubakri__vcore_unpg_ro_end = __vcore_unpg_ro_start + __vcore_unpg_ro_size; 280ebc8e1ffSMarouene Boubakri 281ebc8e1ffSMarouene Boubakri/* Unpaged read-write memory */ 282ebc8e1ffSMarouene Boubakri__vcore_unpg_rw_start = __flatmap_rw_start; 283ebc8e1ffSMarouene Boubakri__vcore_unpg_rw_size = __flatmap_rw_size; 284ebc8e1ffSMarouene Boubakri__vcore_unpg_rw_end = __vcore_unpg_rw_start + __vcore_unpg_rw_size; 285ebc8e1ffSMarouene Boubakri 286a5ac48d6SJens Wiklander__vcore_free_start = __flatmap_free_start; 287a5ac48d6SJens Wiklander__vcore_free_size = __flatmap_free_size; 288a5ac48d6SJens Wiklander__vcore_free_end = __flatmap_free_start + __flatmap_free_size; 289a5ac48d6SJens Wiklander 290ebc8e1ffSMarouene Boubakri#ifdef CFG_CORE_SANITIZE_KADDRESS 291ebc8e1ffSMarouene Boubakri__asan_map_start = (__asan_shadow_start / SMALL_PAGE_SIZE) * 292ebc8e1ffSMarouene Boubakri SMALL_PAGE_SIZE; 293ebc8e1ffSMarouene Boubakri__asan_map_end = ((__asan_shadow_end - 1) / SMALL_PAGE_SIZE) * 294ebc8e1ffSMarouene Boubakri SMALL_PAGE_SIZE + SMALL_PAGE_SIZE; 295ebc8e1ffSMarouene Boubakri__asan_map_size = __asan_map_end - __asan_map_start; 296ebc8e1ffSMarouene Boubakri#endif /*CFG_CORE_SANITIZE_KADDRESS*/ 297