diff --git a/module/atu/include/mod_atu.h b/module/atu/include/mod_atu.h index 13cc7e13f0342966fb7d7615dbcd942bd8323de5..5234a98aa9cd6300b2c274187c9ec5a6a40b8df9 100644 --- a/module/atu/include/mod_atu.h +++ b/module/atu/include/mod_atu.h @@ -1,6 +1,6 @@ /* * Arm SCP/MCP Software - * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -236,7 +236,6 @@ struct mod_atu_api { * * \retval ::FWK_SUCCESS The operation succeeded. * \retval ::FWK_E_PARAM An invalid parameter was encountered. - * - The requested ATU region is not aligned with the ATU page size. * - Invalid attributes configuration data was provided in the `region` * parameter. * - The `region_idx` parameter was null pointer value. @@ -251,6 +250,8 @@ struct mod_atu_api { * region. * \retval ::FWK_E_BUSY The previous message has not been read. * \retval ::FWK_E_DATA Error in the response message received. + * \retval ::FWK_E_ALIGN An invalid alignment was detected. + * - The requested ATU region is not aligned with the ATU page size. */ int (*add_region)( const struct atu_region_map *region, diff --git a/module/atu/src/atu_manage_utils.c b/module/atu/src/atu_manage_utils.c index 4fe3bb2a505cf40853499474215f9819be741805..651257cb8d456d53f479d618e4b5c53cb83c4699 100644 --- a/module/atu/src/atu_manage_utils.c +++ b/module/atu/src/atu_manage_utils.c @@ -1,6 +1,6 @@ /* * Arm SCP/MCP Software - * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -19,6 +19,8 @@ #include +#define GET_PAGE_SIZE(ps) (1ULL << ps) + #if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_INFO static int atu_print_region(uint8_t region_idx, void *device_ctx_ptr) { @@ -219,6 +221,45 @@ int atu_get_available_region_idx(void *device_ctx_ptr, uint8_t *region_idx) return FWK_E_SUPPORT; } +/* + * Validate alignment of phy addr base, logical addr base, and region size + * with ATU page size. In the ATU spec, the term "ps" is used for page size. + * In the ATUBC register, it appears as 0xC, 0xD, or 0xE, corresponding to + * 4KB/8KB/16KB (1 << ps). + */ +int is_region_mapping_aligned(const struct atu_region_map *region, uint8_t ps) +{ + uint64_t page_size = GET_PAGE_SIZE(ps); + + if (FWK_IS_ALIGNED(region->phy_addr_base, page_size) != true) { + FWK_LOG_ERR( + "[ATU] Error! PhyAddr 0x%" PRIX64 " not aligned to (PS:0x%" PRIX64 + ")", + region->phy_addr_base, + page_size); + return FWK_E_ALIGN; + } + + if (FWK_IS_ALIGNED(region->log_addr_base, page_size) != true) { + FWK_LOG_ERR( + "[ATU] Error! LogAddr 0x%" PRIX32 " not aligned to (PS:0x%" PRIX64 + ")", + region->log_addr_base, + page_size); + return FWK_E_ALIGN; + } + + if (FWK_IS_ALIGNED(region->region_size, page_size) != true) { + FWK_LOG_ERR( + "[ATU] Error! Region size 0x%zX not aligned to (PS:0x%" PRIX64 ")", + region->region_size, + page_size); + return FWK_E_ALIGN; + } + + return FWK_SUCCESS; +} + /* Validates if a translation region can be mapped in the ATU */ int atu_validate_region( const struct atu_region_map *region, @@ -235,10 +276,10 @@ int atu_validate_region( ps = device_ctx->page_size; /* Check if the requested region is aligned with the ATU page size */ - if ((region->phy_addr_base & ((1 << ps) - 1)) != 0 || - (region->log_addr_base & ((1 << ps) - 1)) != 0 || - (region->region_size & ((1 << ps) - 1)) != 0) { - return FWK_E_PARAM; + status = is_region_mapping_aligned(region, ps); + + if (status != FWK_SUCCESS) { + return status; } /* diff --git a/module/atu/test/mod_atu_unit_test.c b/module/atu/test/mod_atu_unit_test.c index 99d8cbce380bc36740d25071d2b041ba12d188a2..5c4b8f4f3b3e006541e1b6328f9de286a074a3b8 100644 --- a/module/atu/test/mod_atu_unit_test.c +++ b/module/atu/test/mod_atu_unit_test.c @@ -1,6 +1,6 @@ /* * Arm SCP/MCP Software - * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -265,6 +265,135 @@ void test_atu_add_region_invalid_attributes_config(void) TEST_ASSERT_EQUAL(status, FWK_E_PARAM); } +/*! + * \brief ATU unit test: atu_add_region(), invalid physical address alignment. + * + * \details Handle the case in atu_add_region() where the physical address base + * is not aligned with the ATU page size. This test ensures that + * misaligned physical addresses are correctly detected and handled. + */ +void test_atu_add_region_invalid_phy_addr_alignment(void) +{ + int status; + uint8_t region_idx; + fwk_id_t atu_device_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_ATU, 0); + + /* Define test vectors with their expected results */ + struct { + uint64_t phy_addr_base; + int expected_status; + } test_vec[] = { + { 0x00000001, FWK_E_ALIGN }, { 0xC0000100, FWK_E_ALIGN }, + { 0xC0003001, FWK_E_ALIGN }, { 0xC0006002, FWK_E_ALIGN }, + { (uint64_t)-1, FWK_E_ALIGN }, + }; + + /* Iterate over the test vectors and validate phy_addr alignment */ + for (unsigned int i = 0; i < FWK_ARRAY_SIZE(test_vec); ++i) { + struct atu_region_map region = { + .region_owner_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_ATU), + .log_addr_base = 0x70000000, + .phy_addr_base = test_vec[i].phy_addr_base, + .region_size = 0x400000, + .attributes = ATU_ENCODE_ATTRIBUTES_SECURE_PAS, + }; + + fwk_id_get_element_idx_ExpectAnyArgsAndReturn(0); + + /* Map the address translation region */ + status = atu_add_region(®ion, atu_device_id, ®ion_idx); + + /* Ensure the function returns the expected status */ + TEST_ASSERT_EQUAL(status, test_vec[i].expected_status); + } +} + +/*! + * \brief ATU unit test: atu_add_region(), invalid logical address alignment. + * + * \details Handle the case in atu_add_region() where the logical address base + * is not aligned with the ATU page size. This test ensures that + * misaligned logical addresses are correctly detected and handled. + */ +void test_atu_add_region_invalid_log_addr_alignment(void) +{ + int status; + uint8_t region_idx; + fwk_id_t atu_device_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_ATU, 0); + + /* Define test vectors with their expected results */ + struct { + uint32_t log_addr_base; + int expected_status; + } test_vec[] = { + { 0x00000001, FWK_E_ALIGN }, { 0xF0003001, FWK_E_ALIGN }, + { 0xF0004501, FWK_E_ALIGN }, { 0xF0006002, FWK_E_ALIGN }, + { (uint32_t)-1, FWK_E_ALIGN }, + }; + + /* Iterate over the test vectors and validate log_addr alignment */ + for (unsigned int i = 0; i < FWK_ARRAY_SIZE(test_vec); ++i) { + struct atu_region_map region = { + .region_owner_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_ATU), + .log_addr_base = test_vec[i].log_addr_base, + .phy_addr_base = 0xC0000000, + .region_size = 0x400000, + .attributes = ATU_ENCODE_ATTRIBUTES_SECURE_PAS, + }; + + fwk_id_get_element_idx_ExpectAnyArgsAndReturn(0); + + /* Map the address translation region */ + status = atu_add_region(®ion, atu_device_id, ®ion_idx); + + /* Ensure the function returns the expected status */ + TEST_ASSERT_EQUAL(status, test_vec[i].expected_status); + } +} + +/*! + * \brief ATU unit test: atu_add_region(), invalid region size alignment. + * + * \details Handle the case in atu_add_region() where the region size + * is not aligned with the ATU page size. This test ensures that + * misaligned region sizes are correctly detected and handled. + */ +void test_atu_add_region_invalid_region_size_alignment(void) +{ + int status; + uint8_t region_idx; + fwk_id_t atu_device_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_ATU, 0); + + /* Define test vectors with their expected results */ + struct { + size_t region_size; + int expected_status; + } test_vec[] = { + { 0x00000001, FWK_E_ALIGN }, { 0x00002001, FWK_E_ALIGN }, + { 0x00003FFF, FWK_E_ALIGN }, { 0x00008001, FWK_E_ALIGN }, + { (size_t)-1, FWK_E_ALIGN }, + }; + + /* Iterate over the test vectors and validate region size alignment */ + for (unsigned int i = 0; i < FWK_ARRAY_SIZE(test_vec); ++i) { + struct atu_region_map region = { + .region_owner_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_ATU), + .log_addr_base = 0xF0000000, + .phy_addr_base = 0xC0000000, + .region_size = test_vec[i].region_size, + .attributes = ATU_ENCODE_ATTRIBUTES_SECURE_PAS, + }; + + fwk_id_get_element_idx_ExpectAnyArgsAndReturn(0); + + /* Map the address translation region */ + status = atu_add_region(®ion, atu_device_id, ®ion_idx); + + /* Ensure the function returns the expected status */ + TEST_ASSERT_EQUAL(status, test_vec[i].expected_status); + } +} + /*! * \brief atu unit test: atu_remove_region(), invalid region. * @@ -367,6 +496,9 @@ int atu_test_main(void) RUN_TEST(test_atu_add_region_success); RUN_TEST(test_atu_add_region_overlap); RUN_TEST(test_atu_add_region_invalid_attributes_config); + RUN_TEST(test_atu_add_region_invalid_phy_addr_alignment); + RUN_TEST(test_atu_add_region_invalid_log_addr_alignment); + RUN_TEST(test_atu_add_region_invalid_region_size_alignment); RUN_TEST(test_atu_remove_region_fail); RUN_TEST(test_atu_remove_region_permission_fail); RUN_TEST(test_atu_remove_region_success);