// IBM_PROLOG_BEGIN_TAG // This is an automatically generated prolog. // // $Source: src/kernel/devicesegment.C $ // // IBM CONFIDENTIAL // // COPYRIGHT International Business Machines Corp. 2011 // // p1 // // Object Code Only (OCO) source materials // Licensed Internal Code Source Materials // IBM HostBoot Licensed Internal Code // // The source code for this program is not published or other- // wise divested of its trade secrets, irrespective of what has // been deposited with the U.S. Copyright Office. // // Origin: 30 // // IBM_PROLOG_END #include #include #include #include #include #include #include #include /** * @brief Add the device segment to the SegmentManager. */ void DeviceSegment::init(size_t segId) { kassert((segId >= SegmentManager::MMIO_FIRST_SEGMENT_ID) && (segId <= SegmentManager::MMIO_LAST_SEGMENT_ID)); SegmentManager::addSegment(this, segId); } /** * @brief Handle a page fault for a device address access * @param i_task[in] - Task pointer to the task requiring the page * @param i_addr[in] - 64-bit address needed to be paged * @return bool - TRUE: Page added to page table * FALSE: Not a valid address to be paged */ bool DeviceSegment::handlePageFault(task_t* i_task, uint64_t i_addr) { //Verify input address falls within this segment's address range if (i_addr < this->getBaseAddress() || i_addr >= (this->getBaseAddress() + (1ull << SLBE_s))) { return false; } //Verify the device is mapped uint64_t segment_ea = i_addr - this->getBaseAddress(); size_t idx = segment_ea / ((1ull << SLBE_s) / MMIO_MAP_DEVICES); uint64_t device_offset = segment_ea - (idx * (1ull << SLBE_s) / MMIO_MAP_DEVICES); if (0 == iv_mmioMap[idx].addr || device_offset >= (uint64_t)iv_mmioMap[idx].size) { return false; } PageTableManager::addEntry((i_addr / PAGESIZE) * PAGESIZE, (iv_mmioMap[idx].addr + device_offset) / PAGESIZE, SegmentManager::CI_ACCESS); return true; } /** * @brief Map a device into the device segment. * @param ra[in] - Void pointer to real address to be mapped in * @param i_devDataSize[in] - Size of device segment block * @return void* - Pointer to beginning virtual address, NULL otherwise */ void *DeviceSegment::devMap(void *ra, uint64_t i_devDataSize) { void *segBlock = NULL; if (i_devDataSize <= THIRTYTWO_GB) { //TODO - Use segment block size if/when new device size needed for (size_t i = 0; i < MMIO_MAP_DEVICES; i++) { if (0 == iv_mmioMap[i].addr) { iv_mmioMap[i].size = i_devDataSize; iv_mmioMap[i].addr = reinterpret_cast(ra); //TODO - Use segment block size if/when new device size needed segBlock = reinterpret_cast(i * ((1ull << SLBE_s) / MMIO_MAP_DEVICES) + this->getBaseAddress()); break; } } } else { printk("Unsupported device segment size(0x%lX), ",i_devDataSize); printk("for address 0x%lX\n",reinterpret_cast(ra)); } return segBlock; } int DeviceSegment::devUnmap(void *ea) { int rc = -EINVAL; uint64_t segment_ea = reinterpret_cast(ea); //Verify input address falls within this segment's address range if (segment_ea < this->getBaseAddress() || segment_ea >= (this->getBaseAddress() + (1ull << SLBE_s))) { return rc; } segment_ea = segment_ea - this->getBaseAddress(); //TODO - Calculate idx by segment block size if/when new device size needed size_t idx = segment_ea / ((1ull << SLBE_s) / MMIO_MAP_DEVICES); if (0 != iv_mmioMap[idx].addr) { //Remove all of the defined block's size (<= 32GB) PageTableManager::delRangePN(iv_mmioMap[idx].addr / PAGESIZE, (iv_mmioMap[idx].addr + iv_mmioMap[idx].size) / PAGESIZE); iv_mmioMap[idx].addr = 0; rc = 0; } return rc; }