blob: 0ca9d8b5477e893846a790c6aa3638fdb7a747df [file] [log] [blame]
Alexander Filippoveee98532022-04-28 12:15:42 +03001From 9b91e2ace1c127344e5e7d32ebb866ca636387fd Mon Sep 17 00:00:00 2001
Alexander Amelkineeef3b92020-09-26 04:10:36 +03002From: Alexander Amelkin <alexander@amelkin.msk.ru>
3Date: Wed, 11 Jul 2018 13:16:01 +0300
4Subject: [PATCH] Fix version parsing, update AUX revision info
5
6AUX Revision info was always taken from the dev_id.json
7file if it exists, overriding the value calculated from the
8active firmware version string. Also, when AUX info was
9calculated, it only properly parsed the dirtyness of the
10build.
11
12With this commit the AUX info calculation will properly parse
13the git hash part and will include it as higher 3 bytes of
14the AUX info. For officially released versions the lower byte
15will be zero.
16
17For development versions, bits [7:1] of the fourth byte will
18all be 1 as an indicator of non-release branch. For unofficial
19builds from release branches those bits will contain a number
20from 1 to 126 indicating a patch level since the release tag.
21
22In any case the bit 0 of byte 4 is a dirtyness indicator.
23If the sources used to build the firmware were modified compared
24to the git hash, this bit will be 1.
25
26WARNING: For the AUX decoding from version string to work
27 properly, the dev_id.json file must NOT contain
28 the `aux` property.
29
30Resolves SRV-775
31End-user-impact: Version info is properly represented in the
32 AUX Revision Info fields in response to the
33 IPMI Get Device ID command (ipmitool mc info)
Alexander Filippoveee98532022-04-28 12:15:42 +030034Signed-off-by: Alexander Amelkin <a.amelkin@yadro.com>
35Signed-off-by: Alexander Filippov <a.filippov@yadro.com>
Alexander Amelkineeef3b92020-09-26 04:10:36 +030036
37---
Alexander Filippoveee98532022-04-28 12:15:42 +030038 apphandler.cpp | 226 +++++++++++++++++++++++++++++++++++--------------
39 1 file changed, 163 insertions(+), 63 deletions(-)
Alexander Amelkineeef3b92020-09-26 04:10:36 +030040
41diff --git a/apphandler.cpp b/apphandler.cpp
Alexander Filippoveee98532022-04-28 12:15:42 +030042index 6cdd78f..c62cd35 100644
Alexander Amelkineeef3b92020-09-26 04:10:36 +030043--- a/apphandler.cpp
44+++ b/apphandler.cpp
45@@ -1,4 +1,5 @@
46 #include <arpa/inet.h>
47+#include <endian.h>
48 #include <fcntl.h>
49 #include <limits.h>
50 #include <linux/i2c-dev.h>
51@@ -459,33 +460,112 @@ ipmi::RspType<uint8_t, // acpiSystemPowerState
52 return ipmi::responseSuccess(sysAcpiState, devAcpiState);
53 }
54
55+static
56+std::vector<std::string>
57+tokenize(std::string const& str,
58+ char const token[])
59+{
60+ std::vector<std::string> results;
61+ std::string::size_type j = 0;
62+ while (j < str.length())
63+ {
64+ std::string::size_type k = str.find_first_of(token, j);
65+ if (k == std::string::npos)
66+ k = str.length();
67+ results.push_back(str.substr(j, k-j));
68+ j = k + 1;
69+ }
70+ return results;
71+}
72+
73 typedef struct
74 {
75- char major;
76- char minor;
77- uint16_t d[2];
78+ uint8_t major;
79+ uint8_t minor;
80+ union {
81+ uint8_t aux[4]; // Individual bytes in IPMI big-endian order
82+ uint32_t aux32; // use htobe32() on writes to aux32
83+ };
84 } Revision;
85
86-/* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will */
87-/* return -1 if not in those formats, this routine knows how to parse */
88+/* Currently supports the following formats. It will return -1 if not in */
89+/* those formats: */
90+/* */
91+/* Format 1: */
92 /* version = v0.6-19-gf363f61-dirty */
93-/* ^ ^ ^^ ^ */
94-/* | | |----------|-- additional details */
95-/* | |---------------- Minor */
96-/* |------------------ Major */
97-/* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715 */
98-/* ^ ^ ^^ ^ */
99-/* | | |--|---------- additional details */
100-/* | |---------------- Minor */
101-/* |------------------ Major */
102-/* Additional details : If the option group exists it will force Auxiliary */
103-/* Firmware Revision Information 4th byte to 1 indicating the build was */
104-/* derived with additional edits */
105+/* ^ ^ ^^^^^^ ^^^^^ */
106+/* | | | | */
107+/* | | | `-- AUX dirty flag */
108+/* | | `---------- AUX commit hash */
109+/* | `---------------- Minor */
110+/* `------------------ Major */
111+/* */
112+/* Format 2: */
113+/* version = v1.99.10-113-g65edf7d-r3-0-g9e4f715-dirty */
114+/* ^ ^^ ^^^^^^ ^^^^^ */
115+/* | | | .-----------------' */
116+/* | | | `- AUX dirty flag */
117+/* | | `----- AUX commit hash */
118+/* | `---------------- Minor */
119+/* `------------------ Major */
120+/* */
121+/* version = v2.09-dev-794-g196400c89-some-branch-name-dirty */
122+/* ^ ^^ ^^^^^^ ^^^^^ */
123+/* | | | .-----------------------' */
124+/* | | | `- AUX dirty flag */
125+/* | | `---- AUX commit hash */
126+/* | `---------------- Minor */
127+/* `------------------ Major */
128+/* */
129+/* Format 3 (YADRO Releases): */
130+/* version = v1.0rcf2817p7-rc2-unofficial-dirty */
131+/* ^ ^ ^^^^^^ ^^ .----------^^^^^ */
132+/* | | | | `- AUX dirty flag */
133+/* | | | `------- AUX patch level (1-126), optional */
134+/* | | `-------------- AUX release number */
135+/* | `---------------- Minor */
136+/* `------------------ Major */
137+/* */
138+static
139 int convertVersion(std::string s, Revision& rev)
140 {
141- std::string token;
142- uint16_t commits;
143+ std::vector<std::string> tokens;
144+ bool has_release = false; // version string is of "release" format 3
145+ bool dirty = false;
146
147+ constexpr int TOKEN_MAJOR = 0;
148+ constexpr int TOKEN_MINOR = 1;
149+ // These are for "release" format 3
150+ constexpr int TOKEN_MINOR_HASH = 1;
151+ constexpr int TOKEN_MINOR_PATCH = 2;
152+ // For non-release formats 1 and 2
153+ constexpr int TOKEN_HASH = 3; // Search for git hash starting from this
154+
155+ // Hash info is in the higher 24 bits of AUX F/W Revision Info
156+ constexpr int AUX_HASH_SHIFT = 8;
157+ constexpr int AUX_HASH_LEN = 6;
158+
159+ // Non-release indicator is byte 3 (bits 7..1 of AUX F/W Revision Info)
160+ constexpr int AUX_NON_REL_BYTE = 3;
161+ constexpr int AUX_NON_REL_SHIFT = 1;
162+ constexpr uint8_t AUX_NON_REL_VALUE = UINT8_MAX >> AUX_NON_REL_SHIFT;
163+
164+ // Release patch level occupies the same bits as the non-release indicator
165+ constexpr int AUX_PATCH_BYTE = AUX_NON_REL_BYTE;
166+ constexpr int AUX_PATCH_SHIFT = AUX_NON_REL_SHIFT;
167+ constexpr int AUX_MAX_PATCH = AUX_NON_REL_VALUE - 1;
168+
169+ // The least significant bit of byte 3 is the dirty flag
170+ constexpr int AUX_DIRTY_BYTE = 3;
171+ constexpr int AUX_DIRTY_SHIFT = 0;
172+
173+ // Use base-16 to convert decimals to BCD
174+ constexpr int BCD_BASE = 16;
175+
176+ // First of all clear the revision
177+ rev = {0};
178+
179+ // Cut off the optional 'v' at the beginning
180 auto location = s.find_first_of('v');
181 if (location != std::string::npos)
182 {
Alexander Filippoveee98532022-04-28 12:15:42 +0300183@@ -494,64 +574,77 @@ int convertVersion(std::string s, Revision& rev)
Alexander Amelkineeef3b92020-09-26 04:10:36 +0300184
185 if (!s.empty())
186 {
187- location = s.find_first_of(".");
188- if (location != std::string::npos)
189+ int hash = 0;
190+
191+ if (s.find("dirty") != std::string::npos)
192 {
193- rev.major =
Alexander Filippoveee98532022-04-28 12:15:42 +0300194- static_cast<char>(std::stoi(s.substr(0, location), 0, 10));
Alexander Amelkineeef3b92020-09-26 04:10:36 +0300195- token = s.substr(location + 1);
196+ dirty = true;
197 }
198
199- if (!token.empty())
200+ tokens = tokenize(s, ".-");
201+
202+ if (!tokens.empty())
203 {
204- location = token.find_first_of(".-");
205- if (location != std::string::npos)
206+ rev.major = std::stoi(tokens[TOKEN_MAJOR], 0, BCD_BASE);
207+ }
208+
209+ if (tokens.size() > TOKEN_MINOR)
210+ {
211+ rev.minor = std::stoi(tokens[TOKEN_MINOR], 0, BCD_BASE);
212+
213+ // Minor version token may also contain release/patchlevel info
214+ std::vector<std::string> minortok;
215+
216+ minortok = tokenize(tokens[TOKEN_MINOR], "rp");
217+
218+ if (minortok.size() > TOKEN_MINOR_HASH)
219 {
220- rev.minor = static_cast<char>(
Alexander Filippoveee98532022-04-28 12:15:42 +0300221- std::stoi(token.substr(0, location), 0, 10));
Alexander Amelkineeef3b92020-09-26 04:10:36 +0300222- token = token.substr(location + 1);
223+ // hash is plain hex
224+ hash= std::stoi(minortok[TOKEN_MINOR_HASH], 0, 16);
225+ has_release = true;
226+ }
227+
228+ if (minortok.size() > TOKEN_MINOR_PATCH)
229+ {
230+ // Patch level is encoded as binary, not BCD.
231+ // That is to allow for a wider range.
232+ int pl = std::stoi(minortok[TOKEN_MINOR_PATCH], 0, 10);
233+ uint8_t patchlevel = (pl > AUX_MAX_PATCH)
234+ ? AUX_MAX_PATCH
235+ : pl;
236+ rev.aux[AUX_PATCH_BYTE] = patchlevel << AUX_PATCH_SHIFT;
237 }
238 }
239
240- // Capture the number of commits on top of the minor tag.
241- // I'm using BE format like the ipmi spec asked for
242- location = token.find_first_of(".-");
243- if (!token.empty())
244+ // If it's not a "release" format 3, then search for
245+ // letter 'g' indicating the position of a git hash
246+ // in the version string
247+ if (!has_release && tokens.size() > TOKEN_HASH)
248 {
249- commits = std::stoi(token.substr(0, location), 0, 16);
250- rev.d[0] = (commits >> 8) | (commits << 8);
251-
252- // commit number we skip
253- location = token.find_first_of(".-");
254- if (location != std::string::npos)
255+ std::string hashstr;
256+ for (size_t i = TOKEN_HASH; i < tokens.size(); ++i)
257 {
258- token = token.substr(location + 1);
259+ // Find the first token that looks like a git hash.
260+ // We think here that anything starting with a 'g' is a match.
261+ if ('g' == tokens[i][0])
262+ {
263+ // Cut off the 'g', take only the first AUX_HASH_LEN digits
264+ hashstr = tokens[i].substr(1, AUX_HASH_LEN);
265+ break;
266+ }
267 }
268- }
269- else
270- {
271- rev.d[0] = 0;
272- }
273
274- if (location != std::string::npos)
275- {
276- token = token.substr(location + 1);
277+ // Hash is plain hex
278+ hash = std::stoi(hashstr, 0, 16);
279+ rev.aux[AUX_NON_REL_BYTE] |= AUX_NON_REL_VALUE << AUX_NON_REL_SHIFT;
280 }
281+ rev.aux32 |= htobe32(hash << AUX_HASH_SHIFT);
282+ rev.aux[AUX_DIRTY_BYTE] |= dirty << AUX_DIRTY_SHIFT;
283
284- // Any value of the optional parameter forces it to 1
285- location = token.find_first_of(".-");
286- if (location != std::string::npos)
287- {
288- token = token.substr(location + 1);
289- }
290- commits = (!token.empty()) ? 1 : 0;
291-
292- // We do this operation to get this displayed in least significant bytes
293- // of ipmitool device id command.
294- rev.d[1] = (commits >> 8) | (commits << 8);
295+ return 0;
296 }
297
298- return 0;
299+ return -1;
300 }
301
302 /* @brief: Implement the Get Device ID IPMI command per the IPMI spec
Alexander Filippoveee98532022-04-28 12:15:42 +0300303@@ -623,7 +716,7 @@ ipmi::RspType<uint8_t, // Device ID
Alexander Amelkineeef3b92020-09-26 04:10:36 +0300304
305 rev.minor = (rev.minor > 99 ? 99 : rev.minor);
306 devId.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
307- std::memcpy(&devId.aux, rev.d, 4);
308+ std::memcpy(&devId.aux, rev.aux, 4);
Alexander Filippoveee98532022-04-28 12:15:42 +0300309 haveBMCVersion = true;
Alexander Amelkineeef3b92020-09-26 04:10:36 +0300310 }
Alexander Filippoveee98532022-04-28 12:15:42 +0300311 }
312@@ -643,7 +736,14 @@ ipmi::RspType<uint8_t, // Device ID
Alexander Amelkineeef3b92020-09-26 04:10:36 +0300313 devId.addnDevSupport = data.value("addn_dev_support", 0);
314 devId.manufId = data.value("manuf_id", 0);
315 devId.prodId = data.value("prod_id", 0);
316- devId.aux = data.value("aux", 0);
317+
318+ // Use the AUX data from the file only for overriding
319+ // the data obtained from version string.
320+ if (data.contains("aux"))
321+ {
322+ // AUX F/W Revision Info is MSB first (big-endian)
323+ devId.aux = htobe32(data.value("aux", 0));
324+ }
325
326 // Set the availablitity of the BMC.
327 defaultActivationSetting = data.value("availability", true);