1 /*
2  * Copyright 2018 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "pci_handler.hpp"
18 
19 #include "data.hpp"
20 
21 #include <fcntl.h>
22 #include <linux/aspeed-p2a-ctrl.h>
23 
24 #include <cstdint>
25 #include <cstring>
26 #include <string>
27 #include <vector>
28 
29 namespace ipmi_flash
30 {
31 
32 const std::string PciDataHandler::p2aControlPath = "/dev/aspeed-p2a-ctrl";
33 
34 bool PciDataHandler::open()
35 {
36     mappedFd = sys->open(p2aControlPath.c_str(), O_RDWR);
37     if (mappedFd == -1)
38     {
39         return false;
40     }
41 
42     struct aspeed_p2a_ctrl_mapping map;
43     map.addr = regionAddress;
44     map.length = memoryRegionSize;
45     map.flags = ASPEED_P2A_CTRL_READWRITE;
46 
47     if (sys->ioctl(mappedFd, ASPEED_P2A_CTRL_IOCTL_SET_WINDOW, &map))
48     {
49         sys->close(mappedFd);
50         mappedFd = -1;
51 
52         return false;
53     }
54 
55     if (sys->ioctl(mappedFd, ASPEED_P2A_CTRL_IOCTL_GET_MEMORY_CONFIG, &map))
56     {
57         sys->close(mappedFd);
58         mappedFd = -1;
59 
60         return false;
61     }
62 
63     /* The length of the region reserved is reported, and it's important
64      * because the offset + memory region could be beyond that reserved
65      * region.
66      */
67     std::uint64_t offset = regionAddress - map.addr;
68 
69     mapped = reinterpret_cast<std::uint8_t*>(
70         mmap(0, memoryRegionSize, PROT_READ, MAP_SHARED, mappedFd, offset));
71     if (mapped == MAP_FAILED)
72     {
73         sys->close(mappedFd);
74         mappedFd = -1;
75         mapped = nullptr;
76 
77         return false;
78     }
79 
80     return true;
81 }
82 
83 bool PciDataHandler::close()
84 {
85     /* TODO: Turn off the P2A bridge and region to disable host-side access.
86      */
87     if (mapped)
88     {
89         sys->munmap(mapped, memoryRegionSize);
90         mapped = nullptr;
91     }
92 
93     if (mappedFd != -1)
94     {
95         sys->close(mappedFd);
96         mappedFd = -1;
97     }
98 
99     return true;
100 }
101 
102 std::vector<std::uint8_t> PciDataHandler::copyFrom(std::uint32_t length)
103 {
104     std::vector<std::uint8_t> results(length);
105     std::memcpy(results.data(), mapped, length);
106 
107     return results;
108 }
109 
110 bool PciDataHandler::writeMeta(const std::vector<std::uint8_t>&)
111 {
112     /* PCI handler doesn't require configuration write, only read. */
113     return false;
114 }
115 
116 std::vector<std::uint8_t> PciDataHandler::readMeta()
117 {
118     /* PCI handler does require returning a configuration from read. */
119     struct PciConfigResponse reply;
120     reply.address = regionAddress;
121 
122     std::vector<std::uint8_t> bytes;
123     bytes.resize(sizeof(reply));
124     std::memcpy(bytes.data(), &reply, sizeof(reply));
125 
126     return bytes;
127 }
128 
129 } // namespace ipmi_flash
130