1361fc4cbSmaya/* 2361fc4cbSmaya * Copyright © 2019 Google LLC 3361fc4cbSmaya * 4361fc4cbSmaya * Permission is hereby granted, free of charge, to any person obtaining a 5361fc4cbSmaya * copy of this software and associated documentation files (the "Software"), 6361fc4cbSmaya * to deal in the Software without restriction, including without limitation 7361fc4cbSmaya * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8361fc4cbSmaya * and/or sell copies of the Software, and to permit persons to whom the 9361fc4cbSmaya * Software is furnished to do so, subject to the following conditions: 10361fc4cbSmaya * 11361fc4cbSmaya * The above copyright notice and this permission notice (including the next 12361fc4cbSmaya * paragraph) shall be included in all copies or substantial portions of the 13361fc4cbSmaya * Software. 14361fc4cbSmaya * 15361fc4cbSmaya * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16361fc4cbSmaya * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17361fc4cbSmaya * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18361fc4cbSmaya * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19361fc4cbSmaya * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20361fc4cbSmaya * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21361fc4cbSmaya * DEALINGS IN THE SOFTWARE. 22361fc4cbSmaya */ 23361fc4cbSmaya 24361fc4cbSmaya#include "tu_cs.h" 25361fc4cbSmaya 26361fc4cbSmaya/** 27361fc4cbSmaya * Initialize a command stream. 28361fc4cbSmaya */ 29361fc4cbSmayavoid 307ec681f3Smrgtu_cs_init(struct tu_cs *cs, 317ec681f3Smrg struct tu_device *device, 327ec681f3Smrg enum tu_cs_mode mode, 337ec681f3Smrg uint32_t initial_size) 34361fc4cbSmaya{ 35361fc4cbSmaya assert(mode != TU_CS_MODE_EXTERNAL); 36361fc4cbSmaya 37361fc4cbSmaya memset(cs, 0, sizeof(*cs)); 38361fc4cbSmaya 397ec681f3Smrg cs->device = device; 40361fc4cbSmaya cs->mode = mode; 41361fc4cbSmaya cs->next_bo_size = initial_size; 42361fc4cbSmaya} 43361fc4cbSmaya 44361fc4cbSmaya/** 45361fc4cbSmaya * Initialize a command stream as a wrapper to an external buffer. 46361fc4cbSmaya */ 47361fc4cbSmayavoid 487ec681f3Smrgtu_cs_init_external(struct tu_cs *cs, struct tu_device *device, 497ec681f3Smrg uint32_t *start, uint32_t *end) 50361fc4cbSmaya{ 51361fc4cbSmaya memset(cs, 0, sizeof(*cs)); 52361fc4cbSmaya 537ec681f3Smrg cs->device = device; 54361fc4cbSmaya cs->mode = TU_CS_MODE_EXTERNAL; 55361fc4cbSmaya cs->start = cs->reserved_end = cs->cur = start; 56361fc4cbSmaya cs->end = end; 57361fc4cbSmaya} 58361fc4cbSmaya 59361fc4cbSmaya/** 60361fc4cbSmaya * Finish and release all resources owned by a command stream. 61361fc4cbSmaya */ 62361fc4cbSmayavoid 637ec681f3Smrgtu_cs_finish(struct tu_cs *cs) 64361fc4cbSmaya{ 65361fc4cbSmaya for (uint32_t i = 0; i < cs->bo_count; ++i) { 667ec681f3Smrg tu_bo_finish(cs->device, cs->bos[i]); 67361fc4cbSmaya free(cs->bos[i]); 68361fc4cbSmaya } 69361fc4cbSmaya 70361fc4cbSmaya free(cs->entries); 71361fc4cbSmaya free(cs->bos); 72361fc4cbSmaya} 73361fc4cbSmaya 74361fc4cbSmaya/** 75361fc4cbSmaya * Get the offset of the command packets emitted since the last call to 76361fc4cbSmaya * tu_cs_add_entry. 77361fc4cbSmaya */ 78361fc4cbSmayastatic uint32_t 79361fc4cbSmayatu_cs_get_offset(const struct tu_cs *cs) 80361fc4cbSmaya{ 81361fc4cbSmaya assert(cs->bo_count); 82361fc4cbSmaya return cs->start - (uint32_t *) cs->bos[cs->bo_count - 1]->map; 83361fc4cbSmaya} 84361fc4cbSmaya 85361fc4cbSmaya/* 86361fc4cbSmaya * Allocate and add a BO to a command stream. Following command packets will 87361fc4cbSmaya * be emitted to the new BO. 88361fc4cbSmaya */ 89361fc4cbSmayastatic VkResult 907ec681f3Smrgtu_cs_add_bo(struct tu_cs *cs, uint32_t size) 91361fc4cbSmaya{ 92361fc4cbSmaya /* no BO for TU_CS_MODE_EXTERNAL */ 93361fc4cbSmaya assert(cs->mode != TU_CS_MODE_EXTERNAL); 94361fc4cbSmaya 95361fc4cbSmaya /* no dangling command packet */ 96361fc4cbSmaya assert(tu_cs_is_empty(cs)); 97361fc4cbSmaya 98361fc4cbSmaya /* grow cs->bos if needed */ 99361fc4cbSmaya if (cs->bo_count == cs->bo_capacity) { 100361fc4cbSmaya uint32_t new_capacity = MAX2(4, 2 * cs->bo_capacity); 101361fc4cbSmaya struct tu_bo **new_bos = 102361fc4cbSmaya realloc(cs->bos, new_capacity * sizeof(struct tu_bo *)); 103361fc4cbSmaya if (!new_bos) 104361fc4cbSmaya return VK_ERROR_OUT_OF_HOST_MEMORY; 105361fc4cbSmaya 106361fc4cbSmaya cs->bo_capacity = new_capacity; 107361fc4cbSmaya cs->bos = new_bos; 108361fc4cbSmaya } 109361fc4cbSmaya 110361fc4cbSmaya struct tu_bo *new_bo = malloc(sizeof(struct tu_bo)); 111361fc4cbSmaya if (!new_bo) 112361fc4cbSmaya return VK_ERROR_OUT_OF_HOST_MEMORY; 113361fc4cbSmaya 1147ec681f3Smrg VkResult result = 1157ec681f3Smrg tu_bo_init_new(cs->device, new_bo, size * sizeof(uint32_t), 1167ec681f3Smrg TU_BO_ALLOC_GPU_READ_ONLY | TU_BO_ALLOC_ALLOW_DUMP); 117361fc4cbSmaya if (result != VK_SUCCESS) { 118361fc4cbSmaya free(new_bo); 119361fc4cbSmaya return result; 120361fc4cbSmaya } 121361fc4cbSmaya 1227ec681f3Smrg result = tu_bo_map(cs->device, new_bo); 123361fc4cbSmaya if (result != VK_SUCCESS) { 1247ec681f3Smrg tu_bo_finish(cs->device, new_bo); 125361fc4cbSmaya free(new_bo); 126361fc4cbSmaya return result; 127361fc4cbSmaya } 128361fc4cbSmaya 129361fc4cbSmaya cs->bos[cs->bo_count++] = new_bo; 130361fc4cbSmaya 131361fc4cbSmaya cs->start = cs->cur = cs->reserved_end = (uint32_t *) new_bo->map; 132361fc4cbSmaya cs->end = cs->start + new_bo->size / sizeof(uint32_t); 133361fc4cbSmaya 134361fc4cbSmaya return VK_SUCCESS; 135361fc4cbSmaya} 136361fc4cbSmaya 137361fc4cbSmaya/** 138361fc4cbSmaya * Reserve an IB entry. 139361fc4cbSmaya */ 140361fc4cbSmayastatic VkResult 1417ec681f3Smrgtu_cs_reserve_entry(struct tu_cs *cs) 142361fc4cbSmaya{ 143361fc4cbSmaya /* entries are only for TU_CS_MODE_GROW */ 144361fc4cbSmaya assert(cs->mode == TU_CS_MODE_GROW); 145361fc4cbSmaya 146361fc4cbSmaya /* grow cs->entries if needed */ 147361fc4cbSmaya if (cs->entry_count == cs->entry_capacity) { 148361fc4cbSmaya uint32_t new_capacity = MAX2(4, cs->entry_capacity * 2); 149361fc4cbSmaya struct tu_cs_entry *new_entries = 150361fc4cbSmaya realloc(cs->entries, new_capacity * sizeof(struct tu_cs_entry)); 151361fc4cbSmaya if (!new_entries) 152361fc4cbSmaya return VK_ERROR_OUT_OF_HOST_MEMORY; 153361fc4cbSmaya 154361fc4cbSmaya cs->entry_capacity = new_capacity; 155361fc4cbSmaya cs->entries = new_entries; 156361fc4cbSmaya } 157361fc4cbSmaya 158361fc4cbSmaya return VK_SUCCESS; 159361fc4cbSmaya} 160361fc4cbSmaya 161361fc4cbSmaya/** 162361fc4cbSmaya * Add an IB entry for the command packets emitted since the last call to this 163361fc4cbSmaya * function. 164361fc4cbSmaya */ 165361fc4cbSmayastatic void 166361fc4cbSmayatu_cs_add_entry(struct tu_cs *cs) 167361fc4cbSmaya{ 168361fc4cbSmaya /* entries are only for TU_CS_MODE_GROW */ 169361fc4cbSmaya assert(cs->mode == TU_CS_MODE_GROW); 170361fc4cbSmaya 171361fc4cbSmaya /* disallow empty entry */ 172361fc4cbSmaya assert(!tu_cs_is_empty(cs)); 173361fc4cbSmaya 174361fc4cbSmaya /* 175361fc4cbSmaya * because we disallow empty entry, tu_cs_add_bo and tu_cs_reserve_entry 176361fc4cbSmaya * must both have been called 177361fc4cbSmaya */ 178361fc4cbSmaya assert(cs->bo_count); 179361fc4cbSmaya assert(cs->entry_count < cs->entry_capacity); 180361fc4cbSmaya 181361fc4cbSmaya /* add an entry for [cs->start, cs->cur] */ 182361fc4cbSmaya cs->entries[cs->entry_count++] = (struct tu_cs_entry) { 183361fc4cbSmaya .bo = cs->bos[cs->bo_count - 1], 184361fc4cbSmaya .size = tu_cs_get_size(cs) * sizeof(uint32_t), 185361fc4cbSmaya .offset = tu_cs_get_offset(cs) * sizeof(uint32_t), 186361fc4cbSmaya }; 187361fc4cbSmaya 188361fc4cbSmaya cs->start = cs->cur; 189361fc4cbSmaya} 190361fc4cbSmaya 1917ec681f3Smrg/** 1927ec681f3Smrg * same behavior as tu_cs_emit_call but without the indirect 1937ec681f3Smrg */ 1947ec681f3SmrgVkResult 1957ec681f3Smrgtu_cs_add_entries(struct tu_cs *cs, struct tu_cs *target) 1967ec681f3Smrg{ 1977ec681f3Smrg VkResult result; 1987ec681f3Smrg 1997ec681f3Smrg assert(cs->mode == TU_CS_MODE_GROW); 2007ec681f3Smrg assert(target->mode == TU_CS_MODE_GROW); 2017ec681f3Smrg 2027ec681f3Smrg if (!tu_cs_is_empty(cs)) 2037ec681f3Smrg tu_cs_add_entry(cs); 2047ec681f3Smrg 2057ec681f3Smrg for (unsigned i = 0; i < target->entry_count; i++) { 2067ec681f3Smrg result = tu_cs_reserve_entry(cs); 2077ec681f3Smrg if (result != VK_SUCCESS) 2087ec681f3Smrg return result; 2097ec681f3Smrg cs->entries[cs->entry_count++] = target->entries[i]; 2107ec681f3Smrg } 2117ec681f3Smrg 2127ec681f3Smrg return VK_SUCCESS; 2137ec681f3Smrg} 2147ec681f3Smrg 215361fc4cbSmaya/** 216361fc4cbSmaya * Begin (or continue) command packet emission. This does nothing but sanity 217361fc4cbSmaya * checks currently. \a cs must not be in TU_CS_MODE_SUB_STREAM mode. 218361fc4cbSmaya */ 219361fc4cbSmayavoid 220361fc4cbSmayatu_cs_begin(struct tu_cs *cs) 221361fc4cbSmaya{ 222361fc4cbSmaya assert(cs->mode != TU_CS_MODE_SUB_STREAM); 223361fc4cbSmaya assert(tu_cs_is_empty(cs)); 224361fc4cbSmaya} 225361fc4cbSmaya 226361fc4cbSmaya/** 227361fc4cbSmaya * End command packet emission. This adds an IB entry when \a cs is in 228361fc4cbSmaya * TU_CS_MODE_GROW mode. 229361fc4cbSmaya */ 230361fc4cbSmayavoid 231361fc4cbSmayatu_cs_end(struct tu_cs *cs) 232361fc4cbSmaya{ 233361fc4cbSmaya assert(cs->mode != TU_CS_MODE_SUB_STREAM); 234361fc4cbSmaya 235361fc4cbSmaya if (cs->mode == TU_CS_MODE_GROW && !tu_cs_is_empty(cs)) 236361fc4cbSmaya tu_cs_add_entry(cs); 237361fc4cbSmaya} 238361fc4cbSmaya 239361fc4cbSmaya/** 240361fc4cbSmaya * Begin command packet emission to a sub-stream. \a cs must be in 241361fc4cbSmaya * TU_CS_MODE_SUB_STREAM mode. 242361fc4cbSmaya * 243361fc4cbSmaya * Return \a sub_cs which is in TU_CS_MODE_EXTERNAL mode. tu_cs_begin and 244361fc4cbSmaya * tu_cs_reserve_space are implied and \a sub_cs is ready for command packet 245361fc4cbSmaya * emission. 246361fc4cbSmaya */ 247361fc4cbSmayaVkResult 2487ec681f3Smrgtu_cs_begin_sub_stream(struct tu_cs *cs, uint32_t size, struct tu_cs *sub_cs) 249361fc4cbSmaya{ 250361fc4cbSmaya assert(cs->mode == TU_CS_MODE_SUB_STREAM); 251361fc4cbSmaya assert(size); 252361fc4cbSmaya 2537ec681f3Smrg VkResult result = tu_cs_reserve_space(cs, size); 254361fc4cbSmaya if (result != VK_SUCCESS) 255361fc4cbSmaya return result; 256361fc4cbSmaya 2577ec681f3Smrg tu_cs_init_external(sub_cs, cs->device, cs->cur, cs->reserved_end); 258361fc4cbSmaya tu_cs_begin(sub_cs); 2597ec681f3Smrg result = tu_cs_reserve_space(sub_cs, size); 260361fc4cbSmaya assert(result == VK_SUCCESS); 261361fc4cbSmaya 262361fc4cbSmaya return VK_SUCCESS; 263361fc4cbSmaya} 264361fc4cbSmaya 2657ec681f3Smrg/** 2667ec681f3Smrg * Allocate count*size dwords, aligned to size dwords. 2677ec681f3Smrg * \a cs must be in TU_CS_MODE_SUB_STREAM mode. 2687ec681f3Smrg * 2697ec681f3Smrg */ 2707ec681f3SmrgVkResult 2717ec681f3Smrgtu_cs_alloc(struct tu_cs *cs, 2727ec681f3Smrg uint32_t count, 2737ec681f3Smrg uint32_t size, 2747ec681f3Smrg struct tu_cs_memory *memory) 2757ec681f3Smrg{ 2767ec681f3Smrg assert(cs->mode == TU_CS_MODE_SUB_STREAM); 2777ec681f3Smrg assert(size && size <= 1024); 2787ec681f3Smrg 2797ec681f3Smrg if (!count) 2807ec681f3Smrg return VK_SUCCESS; 2817ec681f3Smrg 2827ec681f3Smrg /* TODO: smarter way to deal with alignment? */ 2837ec681f3Smrg 2847ec681f3Smrg VkResult result = tu_cs_reserve_space(cs, count * size + (size-1)); 2857ec681f3Smrg if (result != VK_SUCCESS) 2867ec681f3Smrg return result; 2877ec681f3Smrg 2887ec681f3Smrg struct tu_bo *bo = cs->bos[cs->bo_count - 1]; 2897ec681f3Smrg size_t offset = align(tu_cs_get_offset(cs), size); 2907ec681f3Smrg 2917ec681f3Smrg memory->map = bo->map + offset * sizeof(uint32_t); 2927ec681f3Smrg memory->iova = bo->iova + offset * sizeof(uint32_t); 2937ec681f3Smrg 2947ec681f3Smrg cs->start = cs->cur = (uint32_t*) bo->map + offset + count * size; 2957ec681f3Smrg 2967ec681f3Smrg return VK_SUCCESS; 2977ec681f3Smrg} 2987ec681f3Smrg 299361fc4cbSmaya/** 300361fc4cbSmaya * End command packet emission to a sub-stream. \a sub_cs becomes invalid 301361fc4cbSmaya * after this call. 302361fc4cbSmaya * 303361fc4cbSmaya * Return an IB entry for the sub-stream. The entry has the same lifetime as 304361fc4cbSmaya * \a cs. 305361fc4cbSmaya */ 306361fc4cbSmayastruct tu_cs_entry 307361fc4cbSmayatu_cs_end_sub_stream(struct tu_cs *cs, struct tu_cs *sub_cs) 308361fc4cbSmaya{ 309361fc4cbSmaya assert(cs->mode == TU_CS_MODE_SUB_STREAM); 310361fc4cbSmaya assert(cs->bo_count); 311361fc4cbSmaya assert(sub_cs->start == cs->cur && sub_cs->end == cs->reserved_end); 312361fc4cbSmaya tu_cs_sanity_check(sub_cs); 313361fc4cbSmaya 314361fc4cbSmaya tu_cs_end(sub_cs); 315361fc4cbSmaya 316361fc4cbSmaya cs->cur = sub_cs->cur; 317361fc4cbSmaya 318361fc4cbSmaya struct tu_cs_entry entry = { 319361fc4cbSmaya .bo = cs->bos[cs->bo_count - 1], 320361fc4cbSmaya .size = tu_cs_get_size(cs) * sizeof(uint32_t), 321361fc4cbSmaya .offset = tu_cs_get_offset(cs) * sizeof(uint32_t), 322361fc4cbSmaya }; 323361fc4cbSmaya 324361fc4cbSmaya cs->start = cs->cur; 325361fc4cbSmaya 326361fc4cbSmaya return entry; 327361fc4cbSmaya} 328361fc4cbSmaya 329361fc4cbSmaya/** 330361fc4cbSmaya * Reserve space from a command stream for \a reserved_size uint32_t values. 331361fc4cbSmaya * This never fails when \a cs has mode TU_CS_MODE_EXTERNAL. 332361fc4cbSmaya */ 333361fc4cbSmayaVkResult 3347ec681f3Smrgtu_cs_reserve_space(struct tu_cs *cs, uint32_t reserved_size) 335361fc4cbSmaya{ 336361fc4cbSmaya if (tu_cs_get_space(cs) < reserved_size) { 337361fc4cbSmaya if (cs->mode == TU_CS_MODE_EXTERNAL) { 338361fc4cbSmaya unreachable("cannot grow external buffer"); 339361fc4cbSmaya return VK_ERROR_OUT_OF_HOST_MEMORY; 340361fc4cbSmaya } 341361fc4cbSmaya 342361fc4cbSmaya /* add an entry for the exiting command packets */ 343361fc4cbSmaya if (!tu_cs_is_empty(cs)) { 344361fc4cbSmaya /* no direct command packet for TU_CS_MODE_SUB_STREAM */ 345361fc4cbSmaya assert(cs->mode != TU_CS_MODE_SUB_STREAM); 346361fc4cbSmaya 347361fc4cbSmaya tu_cs_add_entry(cs); 348361fc4cbSmaya } 349361fc4cbSmaya 3507ec681f3Smrg if (cs->cond_flags) { 3517ec681f3Smrg /* Subtract one here to account for the DWORD field itself. */ 3527ec681f3Smrg *cs->cond_dwords = cs->cur - cs->cond_dwords - 1; 3537ec681f3Smrg 3547ec681f3Smrg /* space for CP_COND_REG_EXEC in next bo */ 3557ec681f3Smrg reserved_size += 3; 3567ec681f3Smrg } 3577ec681f3Smrg 358361fc4cbSmaya /* switch to a new BO */ 359361fc4cbSmaya uint32_t new_size = MAX2(cs->next_bo_size, reserved_size); 3607ec681f3Smrg VkResult result = tu_cs_add_bo(cs, new_size); 361361fc4cbSmaya if (result != VK_SUCCESS) 362361fc4cbSmaya return result; 363361fc4cbSmaya 3647ec681f3Smrg /* if inside a condition, emit a new CP_COND_REG_EXEC */ 3657ec681f3Smrg if (cs->cond_flags) { 3667ec681f3Smrg cs->reserved_end = cs->cur + reserved_size; 3677ec681f3Smrg 3687ec681f3Smrg tu_cs_emit_pkt7(cs, CP_COND_REG_EXEC, 2); 3697ec681f3Smrg tu_cs_emit(cs, cs->cond_flags); 3707ec681f3Smrg 3717ec681f3Smrg cs->cond_dwords = cs->cur; 3727ec681f3Smrg 3737ec681f3Smrg /* Emit dummy DWORD field here */ 3747ec681f3Smrg tu_cs_emit(cs, CP_COND_REG_EXEC_1_DWORDS(0)); 3757ec681f3Smrg } 3767ec681f3Smrg 3777ec681f3Smrg /* double the size for the next bo, also there is an upper 3787ec681f3Smrg * bound on IB size, which appears to be 0x0fffff 3797ec681f3Smrg */ 3807ec681f3Smrg new_size = MIN2(new_size << 1, 0x0fffff); 381361fc4cbSmaya if (cs->next_bo_size < new_size) 382361fc4cbSmaya cs->next_bo_size = new_size; 383361fc4cbSmaya } 384361fc4cbSmaya 385361fc4cbSmaya assert(tu_cs_get_space(cs) >= reserved_size); 386361fc4cbSmaya cs->reserved_end = cs->cur + reserved_size; 387361fc4cbSmaya 388361fc4cbSmaya if (cs->mode == TU_CS_MODE_GROW) { 389361fc4cbSmaya /* reserve an entry for the next call to this function or tu_cs_end */ 3907ec681f3Smrg return tu_cs_reserve_entry(cs); 391361fc4cbSmaya } 392361fc4cbSmaya 393361fc4cbSmaya return VK_SUCCESS; 394361fc4cbSmaya} 395361fc4cbSmaya 396361fc4cbSmaya/** 397361fc4cbSmaya * Reset a command stream to its initial state. This discards all comand 398361fc4cbSmaya * packets in \a cs, but does not necessarily release all resources. 399361fc4cbSmaya */ 400361fc4cbSmayavoid 4017ec681f3Smrgtu_cs_reset(struct tu_cs *cs) 402361fc4cbSmaya{ 403361fc4cbSmaya if (cs->mode == TU_CS_MODE_EXTERNAL) { 404361fc4cbSmaya assert(!cs->bo_count && !cs->entry_count); 405361fc4cbSmaya cs->reserved_end = cs->cur = cs->start; 406361fc4cbSmaya return; 407361fc4cbSmaya } 408361fc4cbSmaya 409361fc4cbSmaya for (uint32_t i = 0; i + 1 < cs->bo_count; ++i) { 4107ec681f3Smrg tu_bo_finish(cs->device, cs->bos[i]); 411361fc4cbSmaya free(cs->bos[i]); 412361fc4cbSmaya } 413361fc4cbSmaya 414361fc4cbSmaya if (cs->bo_count) { 415361fc4cbSmaya cs->bos[0] = cs->bos[cs->bo_count - 1]; 416361fc4cbSmaya cs->bo_count = 1; 417361fc4cbSmaya 418361fc4cbSmaya cs->start = cs->cur = cs->reserved_end = (uint32_t *) cs->bos[0]->map; 419361fc4cbSmaya cs->end = cs->start + cs->bos[0]->size / sizeof(uint32_t); 420361fc4cbSmaya } 421361fc4cbSmaya 422361fc4cbSmaya cs->entry_count = 0; 423361fc4cbSmaya} 424