From 343014b3095462dbec20efe456cbb945d4844b4e Mon Sep 17 00:00:00 2001 From: Nick Bofferding Date: Mon, 2 Mar 2015 18:04:05 -0600 Subject: Prevent out-of-order data access to FSP mailbox memory area - Added system call to map FSP mailbox memory with guard permission - Call new mapping in DMA area init - Propagate guard permission down to MMIO map - Apply guard permission in page fault handler - Updated debug tools to support extra bit in MMIO struct Change-Id: I8335ac7d3ef57e46d4c8b6c2b2a42b8a0bf7c4b0 Backport: release-fips830 Backport: release-fips820 CQ: SW295345 Reviewed-on: http://gfw160.aus.stglabs.ibm.com:8080/gerrit/16307 Tested-by: Jenkins Server Reviewed-by: Brian H. Horton Reviewed-by: Daniel M. Crowell Reviewed-by: A. Patrick Williams III --- src/build/debug/Hostboot/_DebugFrameworkVMM.pm | 41 ++++++++++++++++++++------ src/include/kernel/devicesegment.H | 17 +++++++---- src/include/kernel/segmentmgr.H | 14 +++++++-- src/include/kernel/vmmmgr.H | 9 ++++-- src/include/sys/mm.h | 17 ++++++++++- src/kernel/devicesegment.C | 14 +++++++-- src/kernel/ptmgr.C | 8 ++++- src/kernel/segmentmgr.C | 14 +++++---- src/kernel/syscall.C | 4 ++- src/kernel/vmmmgr.C | 14 +++++---- src/lib/syscall_mm.C | 11 ++++++- src/lib/syscall_mmio.C | 4 +-- src/usr/mbox/mbox_dma_buffer.C | 6 ++-- 13 files changed, 133 insertions(+), 40 deletions(-) diff --git a/src/build/debug/Hostboot/_DebugFrameworkVMM.pm b/src/build/debug/Hostboot/_DebugFrameworkVMM.pm index ef8e71dc4..1f0d3fdc9 100755 --- a/src/build/debug/Hostboot/_DebugFrameworkVMM.pm +++ b/src/build/debug/Hostboot/_DebugFrameworkVMM.pm @@ -6,7 +6,9 @@ # # OpenPOWER HostBoot Project # -# COPYRIGHT International Business Machines Corp. 2011,2014 +# Contributors Listed Below - COPYRIGHT 2012,2015 +# [+] International Business Machines Corp. +# # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -74,8 +76,12 @@ use constant STACK_PREV_STACK_OFFEST => 8; use constant STACK_KEY_OFFSET => 16; use constant STACK_BLOCKPTR_OFFSET => 24; +use constant MMIO_STRUCT_SIZE => 16; use constant MMIO_STRUCT_SEGADDR_OFFSET => 0; use constant MMIO_STRUCT_SEGSIZE_OFFSET => 8; +use constant MMIO_STRUCT_GUARDED_MASK => 0x4000000000000000; +use constant MMIO_STRUCT_NO_CI_MASK => 0x8000000000000000; +use constant MMIO_STRUCT_SIZE_MASK => 0x3FFFFFFFFFFFFFFF; use constant BLOCK_BASE_ADDR => 0; use constant BLOCK_SIZE_OFFSET => 8; @@ -890,14 +896,26 @@ sub printDeviceSegments # loop through all mmio devices max of 32 per device segment. while($curmmiodevice < $maxmmiodevices) { - my $segmentPhyAddr =::read64($mmiostructptr + - $curmmiodevice*16 + - MMIO_STRUCT_SEGADDR_OFFSET, 8); + my $segmentPhyAddr =::read64($mmiostructptr + + $curmmiodevice*MMIO_STRUCT_SIZE + + MMIO_STRUCT_SEGADDR_OFFSET, 8); + + my $segmentSize =::read64($mmiostructptr + + $curmmiodevice*MMIO_STRUCT_SIZE + + MMIO_STRUCT_SEGSIZE_OFFSET, 8); + use bigint; + + # DeviceSegment::devSegData.no_ci + my $no_ci = ( ($segmentSize & MMIO_STRUCT_NO_CI_MASK) + == MMIO_STRUCT_NO_CI_MASK); + + # DeviceSegment::devSegData.guarded + my $guarded = ( ($segmentSize & MMIO_STRUCT_GUARDED_MASK) + == MMIO_STRUCT_GUARDED_MASK); - my $segmentSize =::read64($mmiostructptr + $curmmiodevice*16 - + MMIO_STRUCT_SEGSIZE_OFFSET, 8); - # Segment size is a 63 bit quantity. - $segmentSize = $segmentSize & 0x7FFFFFFFFFFFFFFF; + # Segment size is a 62 bit quantity. + # DeviceSegment::devSegData.size + $segmentSize = ($segmentSize & MMIO_STRUCT_SIZE_MASK); if ($segmentPhyAddr != 0) { @@ -911,7 +929,12 @@ sub printDeviceSegments ::userDisplay (sprintf " MMio Device Size: %X\n" , $segmentSize); - + ::userDisplay (sprintf + " MMio no_ci flag: %u\n" , + $no_ci); + ::userDisplay (sprintf + " MMio guarded flag: %u\n" , + $guarded); } $curmmiodevice = $curmmiodevice + 1; diff --git a/src/include/kernel/devicesegment.H b/src/include/kernel/devicesegment.H index 4adfaf04f..58f381f06 100644 --- a/src/include/kernel/devicesegment.H +++ b/src/include/kernel/devicesegment.H @@ -5,7 +5,9 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* COPYRIGHT International Business Machines Corp. 2011,2014 */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ @@ -70,9 +72,12 @@ class DeviceSegment : public Segment * @param ra[in] - Void pointer to real address to be mapped in * @param i_devDataSize[in] - Size of device segment block * @param i_nonCI[in] - Device should be mapped cacheable instead of CI + * @param i_guarded[in] - Whether to prevent out-of-order acces to + * instructions or data in the segment. Ignored if CI. * @return void* - Pointer to beginning virtual address, NULL otherwise */ - void* devMap(void* ra, uint64_t i_devDataSize, bool i_nonCI); + void* devMap(void* ra, uint64_t i_devDataSize, bool i_nonCI, + bool i_guarded); /** * @brief Unmap a device from the device segment. @@ -95,9 +100,11 @@ class DeviceSegment : public Segment */ struct devSegData { - uint64_t addr; //< Real address assigned to device - uint64_t no_ci:1; //< Device should be mapped non-CI. - uint64_t size:63; //< A particular device's segment block size + uint64_t addr; //< Real address assigned to device + uint64_t no_ci:1; //< Device should be mapped non-CI. + uint64_t guarded:1; //< Whether to prevent out-of-order access to + //< instructions or data in the segment + uint64_t size:62; //< A particular device's segment block size }; /** diff --git a/src/include/kernel/segmentmgr.H b/src/include/kernel/segmentmgr.H index fae616a2e..34bbe8fb6 100644 --- a/src/include/kernel/segmentmgr.H +++ b/src/include/kernel/segmentmgr.H @@ -5,7 +5,9 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* COPYRIGHT International Business Machines Corp. 2011,2014 */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ @@ -138,9 +140,12 @@ class SegmentManager * @param ra[in] - Void pointer to real address to be mapped in * @param i_devDataSize[in] - Size of device segment block * @param i_nonCI[in] - Device should be mapped cacheable instead of CI + * @param i_guarded[in] - Whether to prevent out-of-order acces to + * instructions or data in the segment. Ignored if CI. * @return void* - Pointer to beginning virtual address, NULL otherwise */ - static void* devMap(void* ra, uint64_t i_devDataSize, bool i_nonCI); + static void* devMap(void* ra, uint64_t i_devDataSize, bool i_nonCI, + bool i_guarded); /** * @brief Unmap a device from the device segment @@ -165,8 +170,11 @@ class SegmentManager PageTableManager::UsageStats_t i_stats ); /** See castOutPages */ void _castOutPages(uint64_t i_type); + /** See devMap */ - void* _devMap(void* ra, uint64_t i_devDataSize, bool i_nonCI); + void* _devMap( + void* ra, uint64_t i_devDataSize, bool i_nonCI, bool i_guarded); + /** See devUnmap */ int _devUnmap(void* ea); diff --git a/src/include/kernel/vmmmgr.H b/src/include/kernel/vmmmgr.H index d7b359d32..18897a6b5 100644 --- a/src/include/kernel/vmmmgr.H +++ b/src/include/kernel/vmmmgr.H @@ -110,9 +110,12 @@ class VmmManager * @param ra[in] - Void pointer to real address to be mapped in * @param i_devDataSize[in] - Size of device segment block * @param i_nonCI[in] - Device should be mapped cacheable instead of CI + * @param i_guarded[in] - Whether to prevent out-of-order acces to + * instructions or data in the segment. Ignored if CI. * @return void* - Pointer to beginning virtual address, NULL otherwise */ - static void* devMap(void* ra, uint64_t i_devDataSize, bool i_nonCI); + static void* devMap(void* ra, uint64_t i_devDataSize, bool i_nonCI, + bool i_guarded); /** * @brief Unmap a device from the device segment @@ -253,7 +256,9 @@ class VmmManager int _mmExtend( void ); /** See devMap */ - void* _devMap(void* ra, uint64_t i_devDataSize, bool i_nonCI); + void* _devMap( + void* ra, uint64_t i_devDataSize, bool i_nonCI, bool i_guarded); + /** See devUnmap */ int _devUnmap(void* ea); diff --git a/src/include/sys/mm.h b/src/include/sys/mm.h index 8394439d9..d0db803cc 100644 --- a/src/include/sys/mm.h +++ b/src/include/sys/mm.h @@ -5,7 +5,9 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* COPYRIGHT International Business Machines Corp. 2011,2014 */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ @@ -59,6 +61,7 @@ enum PAGE_PERMISSIONS NO_ALLOCATE_FROM_ZERO = 0x00000020, NO_ACCESS = 0x00000040, BYPASS_HRMOR = 0x00000080, + GUARDED = 0x00000100, }; @@ -132,6 +135,18 @@ int mm_linear_map(void *i_paddr, uint64_t i_size); */ void* mm_block_map(void* i_paddr, uint64_t i_size); +/** @fn mm_guarded_block_map() + * @brief Allocate an arbitrary physical address into the VMM with guarded + * permissions to prevent out-of-order access to instructions and data. + * @note Use mm_block_unmap to unmap a region mapped using + * mm_guarded_block_map. + * @param[in] i_paddr - Physical address of the memory to map. + * @param[in] i_size - Size of memory to map (in bytes). + * + * @return Virtual address or NULL. + */ +void* mm_guarded_block_map(void* i_paddr, uint64_t i_size); + /** @fn mm_block_unmap() * @brief Unallocate a block previously allocated with mm_block_map. * @param[in] i_vaddr - Virtual address of the mapped block. diff --git a/src/kernel/devicesegment.C b/src/kernel/devicesegment.C index 1989bcbd6..6ee5b7412 100644 --- a/src/kernel/devicesegment.C +++ b/src/kernel/devicesegment.C @@ -5,7 +5,9 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* COPYRIGHT International Business Machines Corp. 2011,2014 */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ @@ -73,7 +75,8 @@ bool DeviceSegment::handlePageFault(task_t* i_task, uint64_t i_addr, PageTableManager::addEntry((i_addr / PAGESIZE) * PAGESIZE, (iv_mmioMap[idx].addr + device_offset) / PAGESIZE, (iv_mmioMap[idx].no_ci ? - (BYPASS_HRMOR | WRITABLE) : + (BYPASS_HRMOR | WRITABLE | + ( iv_mmioMap[idx].guarded ? GUARDED : 0) ) : SegmentManager::CI_ACCESS) ); return true; @@ -85,9 +88,12 @@ bool DeviceSegment::handlePageFault(task_t* i_task, uint64_t i_addr, * @param ra[in] - Void pointer to real address to be mapped in * @param i_devDataSize[in] - Size of device segment block * @param i_nonCI[in] - Device should be mapped cacheable instead of CI + * @param i_guarded[in] - Whether to prevent out-of-order acces to + * instructions or data in the segment. Ignored if CI. * @return void* - Pointer to beginning virtual address, NULL otherwise */ -void *DeviceSegment::devMap(void *ra, uint64_t i_devDataSize, bool i_nonCI) +void *DeviceSegment::devMap(void *ra, uint64_t i_devDataSize, bool i_nonCI, + bool i_guarded) { void *segBlock = NULL; if (i_devDataSize <= THIRTYTWO_GB) @@ -97,6 +103,7 @@ void *DeviceSegment::devMap(void *ra, uint64_t i_devDataSize, bool i_nonCI) if ((0 == iv_mmioMap[i].addr) && (0 == iv_mmioMap[i].size)) { iv_mmioMap[i].no_ci = i_nonCI; + iv_mmioMap[i].guarded = i_guarded; iv_mmioMap[i].size = i_devDataSize; iv_mmioMap[i].addr = reinterpret_cast(ra); @@ -137,6 +144,7 @@ int DeviceSegment::devUnmap(void *ea) false); iv_mmioMap[idx].addr = 0; iv_mmioMap[idx].size = 0; + iv_mmioMap[idx].guarded = 0; rc = 0; } diff --git a/src/kernel/ptmgr.C b/src/kernel/ptmgr.C index 98d32035e..959e0c7dd 100644 --- a/src/kernel/ptmgr.C +++ b/src/kernel/ptmgr.C @@ -903,6 +903,12 @@ void PageTableManager::setAccessBits( PageTableEntry* o_pte, // All others are set to 0b0010 o_pte->WIMG = 0b0010; // Memory Coherency Required + // Turn on the guarded access permission if requested + if(i_accessType & GUARDED) + { + o_pte->WIMG |= 0b0001; + } + if (i_accessType & READ_ONLY) { o_pte->pp1_2 = 0b01; // PP=001 @@ -940,7 +946,7 @@ uint64_t PageTableManager::getAccessType( const PageTableEntry* i_pte ) { return SegmentManager::CI_ACCESS; } - else if (i_pte->WIMG == 0b0010) + else if ( (i_pte->WIMG & 0b1110) == 0b0010) { if (i_pte->pp1_2 == 0b00) { diff --git a/src/kernel/segmentmgr.C b/src/kernel/segmentmgr.C index 341605fd6..73caaf76f 100644 --- a/src/kernel/segmentmgr.C +++ b/src/kernel/segmentmgr.C @@ -5,7 +5,9 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* COPYRIGHT International Business Machines Corp. 2011,2014 */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ @@ -63,10 +65,11 @@ void SegmentManager::castOutPages(uint64_t i_type) Singleton::instance()._castOutPages(i_type); } -void* SegmentManager::devMap(void* ra, uint64_t i_devDataSize, bool i_nonCI) +void* SegmentManager::devMap(void* ra, uint64_t i_devDataSize, bool i_nonCI, + bool i_guarded) { return Singleton::instance()._devMap(ra, i_devDataSize, - i_nonCI); + i_nonCI, i_guarded); } int SegmentManager::devUnmap(void* ea) @@ -162,7 +165,8 @@ void SegmentManager::_castOutPages(uint64_t i_type) } } -void* SegmentManager::_devMap(void* ra, uint64_t i_devDataSize, bool i_nonCI) +void* SegmentManager::_devMap(void* ra, uint64_t i_devDataSize, bool i_nonCI, + bool i_guarded) { void* ea = NULL; for (size_t i = MMIO_FIRST_SEGMENT_ID; i <= MMIO_LAST_SEGMENT_ID; i++) @@ -170,7 +174,7 @@ void* SegmentManager::_devMap(void* ra, uint64_t i_devDataSize, bool i_nonCI) if (NULL == iv_segments[i]) continue; ea = reinterpret_cast(iv_segments[i])-> - devMap(ra, i_devDataSize, i_nonCI); + devMap(ra, i_devDataSize, i_nonCI, i_guarded); if (ea != NULL) break; } diff --git a/src/kernel/syscall.C b/src/kernel/syscall.C index 7830f7757..d54d85ae7 100644 --- a/src/kernel/syscall.C +++ b/src/kernel/syscall.C @@ -535,6 +535,7 @@ namespace Systemcalls void *ra = (void*)TASK_GETARG0(t); uint64_t devDataSize = ALIGN_PAGE(TASK_GETARG1(t)); bool cacheable = (0 != TASK_GETARG2(t)); + bool guarded = (0 != TASK_GETARG3(t)); if (TASK_GETARG0(t) & (PAGESIZE - 1)) // ensure address page alignment. { @@ -547,7 +548,8 @@ namespace Systemcalls else { TASK_SETRTN(t, - (uint64_t)VmmManager::devMap(ra,devDataSize,cacheable)); + (uint64_t)VmmManager::devMap( + ra,devDataSize,cacheable,guarded)); } } diff --git a/src/kernel/vmmmgr.C b/src/kernel/vmmmgr.C index 98d752bbb..48675c398 100644 --- a/src/kernel/vmmmgr.C +++ b/src/kernel/vmmmgr.C @@ -5,7 +5,9 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* COPYRIGHT International Business Machines Corp. 2010,2014 */ +/* Contributors Listed Below - COPYRIGHT 2010,2015 */ +/* [+] International Business Machines Corp. */ +/* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ @@ -86,10 +88,11 @@ void VmmManager::flushPageTable( void ) Singleton::instance()._flushPageTable(); } -void* VmmManager::devMap(void* ra, uint64_t i_devDataSize, bool i_nonCI) +void* VmmManager::devMap(void* ra, uint64_t i_devDataSize, bool i_nonCI, + bool i_guarded) { return Singleton::instance()._devMap(ra, i_devDataSize, - i_nonCI); + i_nonCI, i_guarded); } int VmmManager::devUnmap(void* ea) @@ -222,12 +225,13 @@ int VmmManager::_mmExtend(void) return rc; } -void* VmmManager::_devMap(void* ra, uint64_t i_devDataSize, bool i_nonCI) +void* VmmManager::_devMap(void* ra, uint64_t i_devDataSize, bool i_nonCI, + bool i_guarded) { void* ea = NULL; lock.lock(); - ea = SegmentManager::devMap(ra, i_devDataSize, i_nonCI); + ea = SegmentManager::devMap(ra, i_devDataSize, i_nonCI, i_guarded); lock.unlock(); return ea; diff --git a/src/lib/syscall_mm.C b/src/lib/syscall_mm.C index b6b17839a..f6aaa75de 100644 --- a/src/lib/syscall_mm.C +++ b/src/lib/syscall_mm.C @@ -137,7 +137,16 @@ void mm_tolerate_ue(uint64_t i_state) */ void* mm_block_map(void* i_paddr, uint64_t i_size) { - return _syscall3(DEV_MAP, i_paddr, (void*)i_size, (void*)1); + return _syscall4(DEV_MAP, i_paddr, (void*)i_size, (void*)1, (void*)0); +} + +/** + * System call to map an arbitrary physical address into the VMM with guarded + * permissions to prevent out-of-order access to instructions and data + */ +void* mm_guarded_block_map(void* i_paddr, uint64_t i_size) +{ + return _syscall4(DEV_MAP, i_paddr, (void*)i_size, (void*)1, (void*)1); } /** diff --git a/src/lib/syscall_mmio.C b/src/lib/syscall_mmio.C index 541528e3b..535e22d39 100644 --- a/src/lib/syscall_mmio.C +++ b/src/lib/syscall_mmio.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2010,2014 */ +/* Contributors Listed Below - COPYRIGHT 2010,2015 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -33,7 +33,7 @@ using namespace Systemcalls; void* mmio_dev_map(void *ra, uint64_t i_devDataSize) { - return _syscall3(DEV_MAP, ra, (void*)i_devDataSize, (void*)0); + return _syscall4(DEV_MAP, ra, (void*)i_devDataSize, (void*)0, (void*)1); } int mmio_dev_unmap(void *ea) diff --git a/src/usr/mbox/mbox_dma_buffer.C b/src/usr/mbox/mbox_dma_buffer.C index 102e67171..ca333e1c8 100644 --- a/src/usr/mbox/mbox_dma_buffer.C +++ b/src/usr/mbox/mbox_dma_buffer.C @@ -5,7 +5,9 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* COPYRIGHT International Business Machines Corp. 2012,2014 */ +/* Contributors Listed Below - COPYRIGHT 2012,2015 */ +/* [+] International Business Machines Corp. */ +/* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ @@ -183,7 +185,7 @@ void DmaBuffer::initPhysicalArea(void*& io_addr, uint64_t& o_phys) VMM_UNSECURE_RESERVED_MEMORY_BASEADDR; // Allocate a new VMM block for the buffer. - io_addr = mm_block_map(reinterpret_cast(o_phys), + io_addr = mm_guarded_block_map(reinterpret_cast(o_phys), VmmManager::MBOX_DMA_SIZE); // Note: We do not plan on deleting this block, even when the buffer // is destructed, because we have fundamentally changed the -- cgit v1.2.1