blob: 56573e01765b62f2c7a81338c4940141a01e45d9 [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
229
230def downloadSignedFile(rootJobUrl, buildNumber, inFileDir="", specifiedOutFilePath=""):
231
232 url = rootJobUrl + str(buildNumber) + "/api/json?tree=artifacts[relativePath]"
233
234 response = requests.get(url, auth=auth)
235
236 if response.status_code == 200:
237 result = json.loads(response.text)
238 if len(result["artifacts"]) == 0:
239 print("Fail to build, please check jenkins log for detailed error")
240 exit(1)
241 relativePath = result["artifacts"][0]["relativePath"]
242 # http://127.0.0.1:8080/job/Sign_Bl31/46/artifact/46/output/bl31-payload.bin.signed
243 downloadUrl = rootJobUrl + str(buildNumber) + "/artifact/" + "/" + relativePath
244 if specifiedOutFilePath == "":
245 outFilePath = os.path.join(inFileDir, os.path.basename(relativePath))
246 else:
247 outFilePath = specifiedOutFilePath
248 r = requests.get(downloadUrl, auth=auth)
249 with open(outFilePath, "wb") as f:
250 f.write(r.content)
251 print("Download the signed file at " + outFilePath)
252 return 0
253 else:
254 print("Fail to download the signed file")
255 exit(1)
256 return 1
257
258
259def waitForSubmit(type):
260 jobName = getJobName(type)
261
262 while True:
263 queues = server.get_queue_info()
264 inQueue = False
265 if queues:
266 for queue_job_info in queues:
267 if queue_job_info["task"].get("name", "") == jobName:
268 inQueue = True
269 break
270 if inQueue:
271 time.sleep(1)
272 print(
273 "Otherone is signing same firmware as you request. Please wait them to complete."
274 )
275 else:
276 print("It is your turn to submit your signing job now.")
277 break
278
279
280def main():
281 print(sys.argv)
282 init()
283 args = get_args()
284
285 rootJobUrl = getJobRootUrl(args.type)
286
287 waitForSubmit(args.type)
288 lastBuildNumber = getLastBuildNumber(rootJobUrl)
289 submitSignJob(
290 type=args.type,
291 chipType=args.chip,
292 inputFilePath=args.inputFilePath,
293 chipAcsFilePath=args.chipAcsFilePath,
294 taVersion=args.taVersion,
Hangyu Li4d44bba2022-05-11 14:33:08 +0800295 marketId=args.marketId,
Bo Lv9edb6992021-09-06 11:31:00 +0800296 casProvider=args.casProvider,
297 chipVariant=args.chipVariant,
298 ddrType=args.ddrType,
299 keyType=args.keyType,
Tao Zeng4c3f74e2021-09-26 14:09:13 +0800300 extraArgs=args.extraArgs,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800301 testService=args.testService,
Bo Lv9edb6992021-09-06 11:31:00 +0800302 )
303
304 buildNumber = int(lastBuildNumber) + 1
305 print("The jenkins build number: " + str(buildNumber))
306 while True:
307 time.sleep(1)
308 building = queryBuildStatus(rootJobUrl, buildNumber)
309 print("Building Status= " + str(building))
310 if building == "False":
311 print("Build is done. Will start to download the signed file")
312 break
313 inputFileDir = os.path.dirname(args.inputFilePath)
314 downloadSignedFile(rootJobUrl, buildNumber, inputFileDir, args.outFilePath)
315
316
317if __name__ == "__main__":
318 main()