blob: ab946443655e28870cfd241f5fd91ac3773b3695 [file] [log] [blame]
Bo Lv9edb6992021-09-06 11:31:00 +08001#!/usr/bin/env python3
2
3import sys
4import os
5import re
6import requests
7from requests.auth import HTTPBasicAuth
8from urllib.parse import urljoin
9import urllib.request
10import json
11import time
12from os.path import expanduser
13import jenkins
14
15
Hangyu Lib97619c2022-08-01 18:07:35 +080016serverRootUrl = "https://jenkins-sh.amlogic.com/job/Security/job/"
Bo Lv9edb6992021-09-06 11:31:00 +080017homeConfigFilePath = "~/.sign.cfg"
Hangyu Li505e5eb2023-03-28 15:54:19 +080018types = ["ta", "vmxta", "irdetota", "bl32", "bl31", "bl2", "bl2e", "bl2x", "bl40", "aucpufw", "vdecfw"]
Hangyu Li0d2e7052022-11-02 15:26:11 +080019casProviders = ["", "VMX", "nagra", "nagra-dev", "vo-dev", "vo", "gs-dev", "gs", "irdeto"]
Bo Lv9edb6992021-09-06 11:31:00 +080020ddrTypes = ["ddr4", "lpddr4", "ddr3", "lpddr3", "lpddr4_lpddr5"]
21chipVariants = ["general", "nocs-jts-ap", "nocs-prod"]
22
23user = ""
24password = ""
25auth = None
26server = None
27
28
29def init():
30 global user
31 global password
32 global auth
33 global server
34 expandedHomeConfigFilePath = expanduser(homeConfigFilePath)
35 configPyPath = os.path.join(sys.path[0], "config.py")
36 if os.path.exists(expandedHomeConfigFilePath):
37 configFilePath = expandedHomeConfigFilePath
38 elif os.path.exists(configPyPath):
39 configFilePath = configPyPath
40 else:
41 print(
42 "Can't find configuration file. Please create configuration file .sign.cfg at your home directory."
43 )
44 exit(1)
45
46 with open(configFilePath, "r") as configFile:
47 while True:
48 line = configFile.readline()
49 if not line:
50 break
51 words = line.split("=")
52 if words[0].strip() == "user":
53 user = words[1].strip().replace("'", "")
54 elif words[0].strip() == "password":
55 password = words[1].strip().replace("'", "")
56 auth = HTTPBasicAuth(user, password)
57 server = jenkins.Jenkins(
58 "https://jenkins-sh.amlogic.com", username=user, password=password
59 )
60
61
62def get_args():
63 from argparse import ArgumentParser
64
65 parser = ArgumentParser()
66
67 parser.add_argument("--in", dest="inputFilePath", required=True, help="input file")
68 parser.add_argument(
69 "--chipAcsFile", dest="chipAcsFilePath", default="null", help="chip acs file"
70 )
71 parser.add_argument(
72 "--out", dest="outFilePath", type=str, default="", help="output signed file"
73 )
74 parser.add_argument(
75 "-v", "--version", action="version", version="%(prog)s 1.0", help="version"
76 )
77 parser.add_argument("--type", choices=types, default=types[0], required=True)
78 parser.add_argument("--chip", type=str)
79 parser.add_argument("--taVersion", type=int, default=0)
Hangyu Li4d44bba2022-05-11 14:33:08 +080080 parser.add_argument("--marketId", type=str, default="null")
Bo Lv9edb6992021-09-06 11:31:00 +080081 parser.add_argument("--casProvider", choices=casProviders, default=casProviders[0])
82 parser.add_argument("--ddrType", type=str, default=ddrTypes[0])
83 parser.add_argument("--chipVariant", choices=chipVariants, default=chipVariants[0])
84 parser.add_argument("--keyType", type=str, dest="keyType", default="dev-keys")
Tao Zeng4c3f74e2021-09-26 14:09:13 +080085 parser.add_argument("--extraArgs", type=str, default="")
Pengguang Zhu899a8de2021-09-17 15:48:28 +080086 parser.add_argument("--testService", type=int, default=0)
Bo Lv9edb6992021-09-06 11:31:00 +080087
88 return parser.parse_args()
89
90
91def getLastBuildNumber(rootJobUrl):
92 url = urljoin(rootJobUrl, "lastBuild/buildNumber")
93
94 response = requests.get(url, auth=auth)
95
96 if response.status_code == 200:
97 return response.text
98 else:
99 print(
100 "Fail to get last build number due to the error "
101 + str(response.status_code)
102 )
103 return 0
104
105
106def getJobRootUrl(type):
107 if type == "ta":
Hangyu Lib97619c2022-08-01 18:07:35 +0800108 return urljoin(serverRootUrl, "Signing/job/Sign_TA/")
109 elif type == "vmxta":
110 return urljoin(serverRootUrl, "CAS/job/VMX/job/VMX_TA_Sign/")
Hangyu Li505e5eb2023-03-28 15:54:19 +0800111 elif type == "irdetota":
112 return urljoin(serverRootUrl, "Signing/job/Sign_Irdeto_TA_for_s905c1/")
Bo Lv9edb6992021-09-06 11:31:00 +0800113 elif type == "bl31":
Hangyu Lib97619c2022-08-01 18:07:35 +0800114 return urljoin(serverRootUrl, "Signing/job/Sign_Bl31/")
Bo Lv9edb6992021-09-06 11:31:00 +0800115 elif type == "bl2":
Hangyu Lib97619c2022-08-01 18:07:35 +0800116 return urljoin(serverRootUrl, "Signing/job/Sign_Bl2/")
Bo Lv9edb6992021-09-06 11:31:00 +0800117 elif type == "bl32":
Hangyu Lib97619c2022-08-01 18:07:35 +0800118 return urljoin(serverRootUrl, "Signing/job/Sign_Bl32/")
Hangyu Li229ddee2022-03-24 13:57:51 +0800119 elif type == "aucpufw":
Hangyu Lib97619c2022-08-01 18:07:35 +0800120 return urljoin(serverRootUrl, "Signing/job/Sign_AUCPU_FW/")
Hangyu Li22720d52022-03-29 11:07:20 +0800121 elif type == "vdecfw":
Hangyu Lib97619c2022-08-01 18:07:35 +0800122 return urljoin(serverRootUrl, "Signing/job/Sign_VDEC_FW/")
Bo Lv9edb6992021-09-06 11:31:00 +0800123 else: # bl2e, bl2x, bl40
Hangyu Lib97619c2022-08-01 18:07:35 +0800124 return urljoin(serverRootUrl, "Signing/job/Sign_Bl2e_Bl2x_Bl40/")
Bo Lv9edb6992021-09-06 11:31:00 +0800125
126
127def getJobName(type):
128 if type == "ta":
129 return "Sign_TA"
Hangyu Li505e5eb2023-03-28 15:54:19 +0800130 elif type == "vmxta":
131 return "Sign_VMX_TA"
132 elif type == "irdetota":
133 return "Sign_Irdeto_TA_for_s905c1"
Bo Lv9edb6992021-09-06 11:31:00 +0800134 elif type == "bl31":
135 return "Sign_Bl31"
136 elif type == "bl2":
137 return "Sign_Bl2"
138 elif type == "bl32":
139 return "Sign_Bl32"
Hangyu Li229ddee2022-03-24 13:57:51 +0800140 elif type == "aucpufw":
141 return "Sign_AUCPU_FW"
Hangyu Li22720d52022-03-29 11:07:20 +0800142 elif type == "vdecfw":
143 return "Sign_VDEC_FW"
Bo Lv9edb6992021-09-06 11:31:00 +0800144 else: # bl2e, bl2x, bl40
145 return "Sign_Bl2e_Bl2x_Bl40"
146
147
148def submitSignJob(
149 type,
150 chipType,
151 inputFilePath,
152 chipAcsFilePath,
153 taVersion="0",
Hangyu Li4d44bba2022-05-11 14:33:08 +0800154 marketId="",
Bo Lv9edb6992021-09-06 11:31:00 +0800155 casProvider="",
156 chipVariant="",
157 ddrType="",
158 keyType="dev-keys",
Tao Zeng4c3f74e2021-09-26 14:09:13 +0800159 extraArgs="",
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800160 testService=0,
Bo Lv9edb6992021-09-06 11:31:00 +0800161):
162
163 fileName = os.path.basename(inputFilePath)
164 fileParameter = "file"
165 uploadFile = {
166 fileParameter: (fileName, open(inputFilePath, "rb")),
167 }
168 url = getJobRootUrl(type) + "buildWithParameters"
Hangyu Li505e5eb2023-03-28 15:54:19 +0800169 if type == "ta" or type == "vmxta" or type == "irdetota":
Bo Lv9edb6992021-09-06 11:31:00 +0800170 data = {
171 "chip_part_number": chipType,
172 "ta_version": taVersion,
Hangyu Li4d44bba2022-05-11 14:33:08 +0800173 "market_id": marketId,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800174 "testService": testService,
Bo Lv9edb6992021-09-06 11:31:00 +0800175 }
176 elif type == "bl32":
177
178 data = {
179 "chipPartNumber": chipType,
180 "casProvider": casProvider,
181 "keyType": keyType,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800182 "testService": testService,
Bo Lv9edb6992021-09-06 11:31:00 +0800183 }
184
185 elif type == "bl2":
186 chipAcsfileName = os.path.basename(chipAcsFilePath)
187 uploadFile = {
188 fileParameter: (fileName, open(inputFilePath, "rb")),
189 "chipAcsFile": (chipAcsfileName, open(chipAcsFilePath, "rb")),
190 }
191 data = {
192 "chipPartNumber": chipType,
193 "chipVariant": chipVariant,
194 "ddrType": ddrType,
195 "keyType": keyType,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800196 "testService": testService,
Bo Lv9edb6992021-09-06 11:31:00 +0800197 }
Hangyu Li22720d52022-03-29 11:07:20 +0800198 else: # bl2e, bl2x, bl31, bl40, aucpufw, vdecfw
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800199 data = {
200 "chipPartNumber": chipType,
201 "keyType": keyType,
Tao Zeng4c3f74e2021-09-26 14:09:13 +0800202 "extraArgs": extraArgs,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800203 "testService": testService,
204 }
Bo Lv9edb6992021-09-06 11:31:00 +0800205
206 response = requests.post(url, auth=auth, data=data, files=uploadFile)
207
208 if response.status_code == 201:
209 print("Sumbit signing job successfully, please wait...")
210
211 else:
212 print(
213 "Fail to start signing job due to the error: " + str(response.status_code)
214 )
215 exit(1)
216
217
218def queryBuildStatus(rootJobUrl, buildNumber):
219 url = rootJobUrl + str(buildNumber) + "/api/json?tree=building"
220
221 response = requests.get(url, auth=auth)
222
223 if response.status_code == 200:
224 result = json.loads(response.text)
225 return str(result["building"])
226 else:
227 return "NotStart"
228
Hangyu Li4fac3cd2023-07-03 17:39:34 +0800229def queryBuildUser(rootJobUrl, buildNumber):
230 url = rootJobUrl + str(buildNumber) + "/api/json?tree=actions[causes[userId]]"
231
232 response = requests.get(url, auth=auth)
233
234 if response.status_code == 200:
235 result = json.loads(response.text)
236 return str(result["actions"][1]["causes"][0]["userId"])
237 else:
238 return "Error"
Bo Lv9edb6992021-09-06 11:31:00 +0800239
Hangyu Li56e85b62023-09-28 16:08:09 +0800240def checkBuildParameter(rootJobUrl, buildNumber, argsChip, argsCasProvider, argsKeyType):
241 url = rootJobUrl + str(buildNumber) + "/api/json"
242 job = rootJobUrl + str(buildNumber)
243
244 response = requests.get(url, auth=auth)
245 data = response.json()
246
247 # parse JSON data to get chipPartNumber, casProvider, keyType
248 actions = data.get("actions", [])
249 for action in actions:
250 parameters = action.get("parameters", [])
251 for parameter in parameters:
252 if parameter.get("_class") == "hudson.model.StringParameterValue":
253 if parameter.get("name") == "chipPartNumber":
254 chipPartNumber = parameter.get("value")
255 if chipPartNumber != argsChip:
256 print("The jenkins Build job %s chip value doesn't match with yours." % (job))
257 return False
258
259 if parameter.get("name") == "casProvider":
260 casProvider = parameter.get("value")
261 if casProvider != argsCasProvider:
262 print("The jenkins Build job %s casProvider value doesn't match with yours." % (job))
263 return False
264
265 if parameter.get("name") == "keyType":
266 keyType = parameter.get("value")
267 if keyType != argsKeyType:
268 print("The jenkins Build job %s keyType value doesn't match with yours." % (job))
269 return False
270
271 return True
272
Bo Lv9edb6992021-09-06 11:31:00 +0800273def downloadSignedFile(rootJobUrl, buildNumber, inFileDir="", specifiedOutFilePath=""):
274
275 url = rootJobUrl + str(buildNumber) + "/api/json?tree=artifacts[relativePath]"
276
277 response = requests.get(url, auth=auth)
278
279 if response.status_code == 200:
280 result = json.loads(response.text)
281 if len(result["artifacts"]) == 0:
282 print("Fail to build, please check jenkins log for detailed error")
283 exit(1)
284 relativePath = result["artifacts"][0]["relativePath"]
285 # http://127.0.0.1:8080/job/Sign_Bl31/46/artifact/46/output/bl31-payload.bin.signed
286 downloadUrl = rootJobUrl + str(buildNumber) + "/artifact/" + "/" + relativePath
287 if specifiedOutFilePath == "":
288 outFilePath = os.path.join(inFileDir, os.path.basename(relativePath))
289 else:
290 outFilePath = specifiedOutFilePath
291 r = requests.get(downloadUrl, auth=auth)
292 with open(outFilePath, "wb") as f:
293 f.write(r.content)
294 print("Download the signed file at " + outFilePath)
295 return 0
296 else:
297 print("Fail to download the signed file")
298 exit(1)
299 return 1
300
301
302def waitForSubmit(type):
303 jobName = getJobName(type)
304
305 while True:
306 queues = server.get_queue_info()
307 inQueue = False
308 if queues:
309 for queue_job_info in queues:
310 if queue_job_info["task"].get("name", "") == jobName:
311 inQueue = True
312 break
313 if inQueue:
314 time.sleep(1)
315 print(
316 "Otherone is signing same firmware as you request. Please wait them to complete."
317 )
318 else:
319 print("It is your turn to submit your signing job now.")
320 break
321
322
323def main():
324 print(sys.argv)
325 init()
326 args = get_args()
327
328 rootJobUrl = getJobRootUrl(args.type)
329
330 waitForSubmit(args.type)
331 lastBuildNumber = getLastBuildNumber(rootJobUrl)
332 submitSignJob(
333 type=args.type,
334 chipType=args.chip,
335 inputFilePath=args.inputFilePath,
336 chipAcsFilePath=args.chipAcsFilePath,
337 taVersion=args.taVersion,
Hangyu Li4d44bba2022-05-11 14:33:08 +0800338 marketId=args.marketId,
Bo Lv9edb6992021-09-06 11:31:00 +0800339 casProvider=args.casProvider,
340 chipVariant=args.chipVariant,
341 ddrType=args.ddrType,
342 keyType=args.keyType,
Tao Zeng4c3f74e2021-09-26 14:09:13 +0800343 extraArgs=args.extraArgs,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800344 testService=args.testService,
Bo Lv9edb6992021-09-06 11:31:00 +0800345 )
346
347 buildNumber = int(lastBuildNumber) + 1
348 print("The jenkins build number: " + str(buildNumber))
349 while True:
350 time.sleep(1)
351 building = queryBuildStatus(rootJobUrl, buildNumber)
352 print("Building Status= " + str(building))
353 if building == "False":
Hangyu Li4fac3cd2023-07-03 17:39:34 +0800354 userId = queryBuildUser(rootJobUrl, buildNumber)
355 if userId.lower() != user.lower():
356 print("The jenkins Build number user name doesn't match yours.It may be caused by too many signing requests submit to the same job.")
357 print("Please wait a moment, and try again.")
358 exit(1)
359
Hangyu Li56e85b62023-09-28 16:08:09 +0800360 status = checkBuildParameter(rootJobUrl, buildNumber, args.chip, args.casProvider, args.keyType)
361 if status == False:
362 print("It may be caused by too many signing requests submit to the same job.")
363 print("Please wait a moment, and try again.")
364 exit(1)
365
Bo Lv9edb6992021-09-06 11:31:00 +0800366 print("Build is done. Will start to download the signed file")
367 break
Hangyu Li4fac3cd2023-07-03 17:39:34 +0800368
Bo Lv9edb6992021-09-06 11:31:00 +0800369 inputFileDir = os.path.dirname(args.inputFilePath)
370 downloadSignedFile(rootJobUrl, buildNumber, inputFileDir, args.outFilePath)
371
372
373if __name__ == "__main__":
374 main()