blob: 8e76a099c32def4154689df015cf0de7550648fe [file] [log] [blame]
Simon Glass02741682013-05-26 07:07:58 -07001#!/usr/bin/python
2
3# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""crosfw - Chrome OS Firmware build/flash script.
8
9Builds a firmware image for any board and writes it to the board. The image
10can be pure upstream or include Chrome OS components (-V). Some device
11tree parameters can be provided, including silent console (-C) and secure
12boot (-S). Use -i for a faster incremental build. The image is written to
13the board by default using USB/em100 (or sdcard with -x). Use -b to specify
14the board to build. Options can be added to ~/.crosfwrc - see the script for
15details.
16
17It can also flash SPI by writing a 'magic flasher' U-Boot with a payload
18to the board.
19
20Usage: crosfw [options]
21
22The script is normally run from within the U-Boot directory which is
23.../src/third_party/u-boot/files
24
25Example 1: Build upstream image for coreboot and write to a 'link':
26
27 crosfw -b link
28
29Example 2: Build verified boot image (V) for daisy/snow and boot in secure
30 mode (S) so that breaking in on boot is not possible.
31
32 crosfw -b daisy -VS
33 crosfw -b daisy -VSC (no console output)
34
35Example 3: Build a magic flasher (F) with full verified boot for peach_pit,
36 but with console enabled, write to SD card (x)
37
38 crosfw -b peach_pit -VSFx
39
40This sript does not use an ebuild. It does a similar thing to the
41chromeos-u-boot ebuild, and runs cros_bundle_firmware to produce various
42types of image, a little like the chromeos-bootimage ebuild.
43
44The purpose of this script is to make it easier and faster to perform
45common firmware build tasks without changing boards, manually updating
46device tree files or lots of USE flags and complexity in the ebuilds.
47
48This script has been tested with snow, link and peach_pit. It builds for
49peach_pit by default. Note that it will also build any upstream ARM
50board - e.g. "-b snapper9260" will build an image for that board.
51
52Mostly you can use the script inside and outside the chroot. The main
53limitation is that dut-control doesn't really work outside the chroot,
54so writing the image to the board over USB is not possible, nor can the
55board be automatically reset on x86 platforms.
56
57For an incremental build (faster), run with -i
58
59To get faster clean builds, install ccache, and create ~/.crosfwrc with
60this line:
61
62 use_ccache = True
63
64(make sure ~/.ccache is not on NFS, or set CCACHE_DIR)
65
66Other options are the default board to build, and verbosity (0-4), e.g.:
67
68 default_board = 'daisy'
69 verbose = 1
70
71It is possible to use multiple servo boards, each on its own port. Add
72these lines to your ~/.crosfwrc to set the servo port to use for each
73board:
74
75 SERVO_PORT['link'] = 8888
76 SERVO_PORT['daisy'] = 9999
77 SERVO_PORT['peach_pit'] = 7777
78
79All builds appear in the b/<board> subdirectory and images are written
80to b/<uboard>/out, where <uboard> is the U-Boot name for the board (in the
81U-Boot boards.cfg file)
82
83For the -a option here are some useful options:
84
85--add-blob cros-splash /dev/null
86--gbb-flags -force-dev-switch-on
87--add-node-enable /spi@131b0000/cros-ecp@0 1
88--verify --full-erase
89--bootcmd "cros_test sha"
90--gbb-flags -force-dev-switch-on
91--bmpblk ~/trunk/src/third_party/u-boot/bmp.bin
92
93For example: -a "--gbb-flags -force-dev-switch-on"
94
95Note the standard bmpblk is at:
96 /home/$USER/trunk/src/third_party/chromiumos-overlay/sys-boot/
97 chromeos-bootimage/files/bmpblk.bin"
98"""
99
100import glob
101import logging
102import multiprocessing
103import os
104import re
105import sys
106
107from chromite.buildbot import constants
108from chromite.lib import commandline
109from chromite.lib import cros_build_lib
110from chromite.lib import osutils
111from chromite.lib import parallel
112
113
114arch = None
115board = None
116compiler = None
117default_board = None
118family = None
119in_chroot = True
120
121logger = logging.getLogger('chromite')
122logging.basicConfig(format='%(message)s')
123kwargs = {'print_cmd': False, 'error_code_ok': True,
124 'debug_level': logger.getEffectiveLevel()}
125
126outdir = ''
127
128 # If you have multiple boards connected on different servo ports, put lines
129# like 'SERVO_PORT{"peach_pit"} = 7777' in your ~/.crosfwrc
130SERVO_PORT = {}
131
132smdk = None
133src_root = os.path.join(constants.SOURCE_ROOT, 'src')
134in_chroot = cros_build_lib.IsInsideChroot()
135
136uboard = ''
137
138default_board = 'peach_pit'
139use_ccache = False
140vendor = None
141verbose = False
142
143# Special cases for the U-Boot board config, the SOCs and default device tree
144# since the naming is not always consistent.
145# x86 has a lot of boards, but to U-Boot they are all the same
146UBOARDS = {
147 'daisy': 'smdk5250',
148 'peach': 'smdk5420',
149}
150for b in ['alex', 'butterfly', 'emeraldlake2', 'link', 'lumpy', 'parrot',
151 'stout', 'stumpy']:
152 UBOARDS[b] = 'coreboot-x86'
153 UBOARDS['chromeos_%s' % b] = 'chromeos_coreboot'
154
155SOCS = {
156 'coreboot-x86': '',
157 'chromeos_coreboot': '',
158 'daisy': 'exynos5250-',
159 'peach': 'exynos5420-',
160}
161
162DEFAULT_DTS = {
163 'daisy': 'snow',
164 'daisy_spring': 'spring',
165 'peach_pit': 'peach-pit',
166}
167
168rc_file = os.path.expanduser('~/.crosfwrc')
169if os.path.exists(rc_file):
170 execfile(rc_file)
171
172
173def Log(msg):
174 """Print out a message if we are in verbose mode.
175
176 Args:
177 msg: Message to print
178 """
179 if verbose:
180 cros_build_lib.Info(msg)
181
182
183def Dumper(flag, infile, outfile):
184 """Run objdump on an input file.
185
186 Args:
187 flag: Flag to pass objdump (e.g. '-d').
188 infile: Input file to process.
189 outfile: Output file to write to.
190 """
191 result = cros_build_lib.RunCommand(
192 [CompilerTool('objdump'), flag, infile],
193 log_stdout_to_file=outfile, **kwargs)
194 if result.returncode:
195 sys.exit()
196
197
198def CompilerTool(tool):
199 """Returns the cross-compiler tool filename.
200
201 Args:
202 tool: Tool name to return, e.g. 'size'.
203
204 Returns:
205 Filename of requested tool.
206 """
207 return '%s%s' % (compiler, tool)
208
209
210def ParseCmdline(argv):
211 """Parse all command line options.
212
213 Args:
214 argv: Arguments to parse.
215
216 Returns:
217 Tuple containing:
218 options: Command line options from optpase
219 args: List of command line arguments
220 """
221 parser = commandline.OptionParser(__doc__)
222 parser.add_option('-a', '--cbfargs', action='append',
223 help='Pass extra arguments to cros_bundle_firmware')
224 parser.add_option('-b', '--board', type='string', default=default_board,
225 help='Select board to build (daisy/peach_pit/link)')
226 parser.add_option('-B', '--build', action='store_false', default=True,
227 help="Don't build U-Boot, just configure device tree")
228 parser.add_option('-C', '--console', action='store_false', default=True,
229 help='Permit console output')
230 parser.add_option('-d', '--dt', default='seaboard',
231 help='Select name of device tree file to use')
232 parser.add_option('-D', '--nodefaults', dest='use_defaults',
233 action='store_false', default=True,
234 help="Don't select default filenames for those not given")
235 parser.add_option('-F', '--flash', action='store_true', default=False,
236 help='Create magic flasher')
237 parser.add_option('-i', '--incremental', action='store_true', default=False,
238 help="Don't reconfigure and clean")
239 parser.add_option('-k', '--kernel', action='store_true', default=False,
240 help='Send kernel to board also')
241 parser.add_option('-O', '--objdump', action='store_true', default=False,
242 help='Write disassembly output')
243 parser.add_option('-r', '--run', action='store_false', default=True,
244 help='Run the boot command')
245 parser.add_option('--ro', action='store_true', default=False,
246 help='Create Chrome OS read-only image')
247 parser.add_option('--rw', action='store_true', default=False,
248 help='Create Chrome OS read-write image')
249 parser.add_option('-s', '--separate', action='store_false', default=True,
250 help='Link device tree into U-Boot, instead of separate')
251 parser.add_option('-S', '--secure', action='store_true', default=False,
252 help='Use vboot_twostop secure boot')
253 parser.add_option('--small', action='store_true', default=False,
254 help='Create Chrome OS small image')
255 parser.add_option('-t', '--trace', action='store_true', default=False,
256 help='Enable trace support')
257 parser.add_option('-v', '--verbose', type='int', default=0,
258 help='Make cros_bundle_firmware verbose')
259 parser.add_option('-V', '--verified', action='store_true', default=False,
260 help='Include Chrome OS verified boot components')
261 parser.add_option('-w', '--write', action='store_false', default=True,
262 help="Don't write image to board using usb/em100")
263 parser.add_option('-x', '--sdcard', action='store_true', default=False,
264 help='Write to SD card instead of USB/em100')
265 parser.add_option('-z', '--size', action='store_true', default=False,
266 help='Display U-Boot image size')
267 return parser.parse_args(argv)
268
269
270def SetupBuild(options):
271 """Set up parameters needed for the build.
272
273 This checks the current environment and options and sets up various things
274 needed for the build, including 'base' which holds the base flags for
275 passing to the U-Boot Makefile.
276
277 Args:
278 options: Command line options
279
280 Returns:
281 Base flags to use for U-Boot, as a list.
282 """
283 global arch, board, compiler, family, outdir, smdk, uboard, vendor, verbose
284
285 if not verbose:
286 verbose = options.verbose != 0
287
288 logger.setLevel(options.verbose)
289
290 Log('Building for %s' % options.board)
291
Simon Glass9d9bf942013-07-10 16:32:42 -0700292 # Separate out board_variant string: "peach_pit" becomes "peach", "pit".
293 # But don't mess up upstream boards which use _ in their name.
Simon Glass02741682013-05-26 07:07:58 -0700294 parts = options.board.split('_')
Simon Glass9d9bf942013-07-10 16:32:42 -0700295 if parts[0] in ['daisy', 'peach']:
296 board = parts[0]
297 else:
298 board = options.board
Simon Glass02741682013-05-26 07:07:58 -0700299
300 # To allow this to be run from 'cros_sdk'
301 if in_chroot:
302 os.chdir(os.path.join(src_root, 'third_party', 'u-boot', 'files'))
303
304 base_board = board
305
306 if options.verified:
307 base_board = 'chromeos_%s' % base_board
308
309 uboard = UBOARDS.get(base_board, base_board)
310 Log('U-Boot board is %s' % uboard)
311
312 # Pull out some information from the U-Boot boards config file
313 family = None
314 with open('boards.cfg') as f:
315 for line in f:
316 if uboard in line:
317 if line[0] == '#':
318 continue
319 fields = line.split()
320 if not fields:
321 continue
322 arch = fields[1]
323 fields += [None, None, None]
324 smdk = fields[3]
325 vendor = fields[4]
326 family = fields[5]
327 break
328 if not arch:
329 cros_build_lib.Die("Selected board '%s' not found in boards.cfg." % board)
330
331 vboot = os.path.join('build', board, 'usr')
332 if arch == 'x86':
333 family = 'em100'
334 if in_chroot:
335 compiler = 'i686-pc-linux-gnu-'
336 else:
337 compiler = '/opt/i686/bin/i686-unknown-elf-'
338 elif arch == 'arm':
339 if in_chroot:
340 # Use the Chrome OS toolchain
341 compiler = 'armv7a-cros-linux-gnueabi-'
342 else:
343 compiler = glob.glob('/opt/linaro/gcc-linaro-arm-linux-*/bin/*gcc')
344 if not compiler:
345 cros_build_lib.Die("""Please install an ARM toolchain for your machine.
346'Install a Linaro toolchain from:'
347'https://launchpad.net/linaro-toolchain-binaries'
348'or see cros/commands/cros_chrome_sdk.py.""")
349 compiler = compiler[0]
350 compiler = re.sub('gcc$', '', compiler)
351 elif arch == 'sandbox':
352 compiler = ''
353 else:
354 cros_build_lib.Die("Selected arch '%s' not supported." % arch)
355
356 if not options.build:
357 options.incremental = True
358
359 cpus = multiprocessing.cpu_count()
360
361 outdir = 'b/%s' % uboard
362 base = [
363 'make',
364 '-j%d' % cpus,
365 'O=%s' % outdir,
366 'ARCH=%s' % arch,
367 'CROSS_COMPILE=%s' % compiler,
368 '--no-print-directory',
369 'HOSTSTRIP=true',
370 'DEV_TREE_SRC=%s-%s' % (family, options.dt),
371 'QEMU_ARCH=']
372
373 if options.verbose < 2:
374 base.append('-s')
375
376 if options.ro and options.rw:
377 cros_build_lib.Die('Cannot specify both --ro and --rw options')
378 if options.ro:
379 base.append('CROS_RO=1')
380 options.small = True
381
382 if options.rw:
383 base.append('CROS_RW=1')
384 options.small = True
385
386 if options.small:
387 base.append('CROS_SMALL=1')
388 else:
389 base.append('CROS_FULL=1')
390
391 if options.verified:
392 base += [
393 'VBOOT=%s' % vboot,
394 'MAKEFLAGS_VBOOT=DEBUG=1',
395 'QUIET=1',
396 'CFLAGS_EXTRA_VBOOT=-DUNROLL_LOOPS',
397 'VBOOT_SOURCE=%s/platform/vboot_reference' % src_root]
Simon Glass9d9bf942013-07-10 16:32:42 -0700398 base.append('VBOOT_DEBUG=1')
Simon Glass02741682013-05-26 07:07:58 -0700399
400 # Handle the Chrome OS USE_STDINT workaround. Vboot needs <stdint.h> due
401 # to a recent change, the need for which I didn't fully understand. But
402 # U-Boot doesn't normally use this. We have added an option to U-Boot to
403 # enable use of <stdint.h> and without it vboot will fail to build. So we
404 # need to enable it where ww can. We can't just enable it always since
405 # that would prevent this script from building other non-Chrome OS boards
406 # with a different (older) toolchain, or Chrome OS boards without vboot.
407 # So use USE_STDINT if the toolchain supports it, and not if not. This
408 # file was originally part of glibc but has recently migrated to the
409 # compiler so it is reasonable to use it with a stand-alone program like
410 # U-Boot. At this point the comment has got long enough that we may as
411 # well include some poetry which seems to be sorely lacking the code base,
412 # so this is from Ogden Nash:
413 # To keep your marriage brimming
414 # With love in the loving cup,
415 # Whenever you're wrong, admit it;
416 # Whenever you're right, shut up.
417 cmd = [CompilerTool('gcc'), '-ffreestanding', '-x', 'c', '-c', '-']
418 result = cros_build_lib.RunCommandCaptureOutput(cmd,
419 input='#include <stdint.h>',
420 **kwargs)
421 if result.returncode == 0:
422 base.append('USE_STDINT=1')
423
424 if options.trace:
425 base.append('FTRACE=1')
426 if options.separate:
427 base.append('DEV_TREE_SEPARATE=1')
428
429 if options.incremental:
430 # Get the correct board for cros_write_firmware
431 config_mk = '%s/include/config.mk' % outdir
432 if not os.path.exists(config_mk):
433 cros_build_lib.Warning('No build found for %s - dropping -i' % board)
434 options.incremental = False
435
436 config_mk = 'include/config.mk'
437 if os.path.exists(config_mk):
438 cros_build_lib.Warning("Warning: '%s' exists, try 'make distclean'"
439 % config_mk)
440
441 # For when U-Boot supports ccache
442 # See http://patchwork.ozlabs.org/patch/245079/
443 if use_ccache:
444 os.environ['CCACHE'] = 'ccache'
445
446 return base
447
448
449def RunBuild(options, base, target, queue):
450 """Run the U-Boot build.
451
452 Args:
453 options: Command line options.
454 base: Base U-Boot flags.
455 target: Target to build.
456 queue: A parallel queue to add jobs to.
457 """
458 Log('U-Boot build flags: %s' % ' '.join(base))
459
460 # Reconfigure U-Boot.
461 if not options.incremental:
462 # Ignore any error from this, some older U-Boots fail on this.
463 cros_build_lib.RunCommand(base + ['distclean'], **kwargs)
464 result = cros_build_lib.RunCommand(base + ['%s_config' % uboard], **kwargs)
465 if result.returncode:
466 sys.exit()
467
468 # Do the actual build.
469 if options.build:
470 result = cros_build_lib.RunCommand(base + [target], **kwargs)
471 if result.returncode:
472 sys.exit()
473
474 files = ['%s/u-boot' % outdir]
475 spl = glob.glob('%s/spl/u-boot-spl' % outdir)
476 if spl:
477 files += spl
478 if options.size:
479 cros_build_lib.RunCommand([CompilerTool('size')] + files, **kwargs)
480 if result.returncode:
481 sys.exit()
482
483 # Create disassembly files .dis and .Dis (full dump)
484 for f in files:
485 base = os.path.splitext(f)[0]
486 if options.objdump:
487 queue.put(('-d', f, base + '.dis'))
488 queue.put(('-D', f, base + '.Dis'))
489 else:
490 # Remove old files which otherwise might be confusing
491 osutils.SafeUnlink(base + '.dis')
492 osutils.SafeUnlink(base + '.Dis')
493
494 Log('Output directory %s' % outdir)
495
496
497def WriteFirmware(options):
498 """Write firmware to the board.
499
500 This uses cros_bundle_firmware to create a firmware image and write it to
501 the board.
502
503 Args:
504 options: Command line options
505 """
506 flash = []
507 kernel = []
508 run = []
509 secure = []
510 servo = []
511 silent = []
512 verbose_arg = []
513
514 bl2 = ['--bl2', '%s/spl/%s-spl.bin' % (outdir, smdk)]
515
516 if options.use_defaults:
517 bl1 = []
518 bmpblk = []
519 ecro = []
520 ecrw = []
521 defaults = []
522 else:
523 bl1 = ['--bl1', '##/build/%s/firmware/u-boot.bl1.bin' % options.board]
524 bmpblk = ['--bmpblk', '##/build/%s/firmware/bmpblk.bin' % options.board]
525 ecro = ['--ecro', '##/build/%s/firmware/ec.RO.bin' % options.board]
526 ecrw = ['--ec', '##/build/%s/firmware/ec.RW.bin' % options.board]
527 defaults = ['-D']
528
529 if arch == 'x86':
530 seabios = ['--seabios',
531 '##/build/%s/firmware/seabios.cbfs' % options.board]
532 else:
533 seabios = []
534
535 if options.sdcard:
536 dest = 'sd:.'
537 elif arch == 'x86':
538 dest = 'em100'
539 elif arch == 'sandbox':
540 dest = ''
541 else:
542 dest = 'usb'
543
544 port = SERVO_PORT.get(options.board, '')
545 if port:
546 servo = ['--servo', '%d' % port]
547
548 if options.flash:
549 flash = ['-F', 'spi']
550
551 if options.verbose:
552 verbose_arg = ['-v', '%s' % options.verbose]
553
554 if options.secure:
555 secure += ['--bootsecure', '--bootcmd', 'vboot_twostop']
556
557 if not options.verified:
558 # Make a small image, without GBB, etc.
559 secure.append('-s')
560
561 if options.kernel:
562 kernel = ['--kernel', '##/build/%s/boot/vmlinux.uimg' % options.board]
563
564 if not options.console:
565 silent = ['--add-config-int', 'silent-console', '1']
566
567 if not options.run:
568 run = ['--bootcmd', 'none']
569
570 if arch != 'sandbox' and not in_chroot and servo:
571 if dest == 'usb':
572 cros_build_lib.Warning('Image cannot be written to board')
573 dest = ''
574 servo = []
575 elif dest == 'em100':
576 cros_build_lib.Warning('Please reset the board manually to boot firmware')
577 servo = []
578
579 if not servo:
580 cros_build_lib.Warning('(sadly dut-control does not work outside chroot)')
581
582 if dest:
583 dest = ['-w', dest]
584 else:
585 dest = []
586
587 soc = SOCS.get(board)
588 if not soc:
589 soc = SOCS.get(uboard, '')
590 dt_name = DEFAULT_DTS.get(options.board, options.board)
591 dts_file = 'board/%s/dts/%s%s.dts' % (vendor, soc, dt_name)
592 Log('Device tree: %s' % dts_file)
593
594 if arch == 'sandbox':
595 uboot_fname = '%s/u-boot' % outdir
596 else:
597 uboot_fname = '%s/u-boot.bin' % outdir
598
599 cbf = ['%s/platform/dev/host/cros_bundle_firmware' % src_root,
600 '-b', options.board,
601 '-d', dts_file,
602 '-I', 'arch/%s/dts' % arch, '-I', 'cros/dts',
603 '-u', uboot_fname,
604 '-O', '%s/out' % outdir,
605 '-M', family]
606
607 for other in [bl1, bl2, bmpblk, defaults, dest, ecro, ecrw, flash, kernel,
608 run, seabios, secure, servo, silent, verbose_arg]:
609 if other:
610 cbf += other
611 if options.cbfargs:
612 for item in options.cbfargs:
613 cbf += item.split(' ')
614 os.environ['PYTHONPATH'] = ('%s/platform/dev/host/lib:%s/..' %
615 (src_root, src_root))
616 Log(' '.join(cbf))
617 result = cros_build_lib.RunCommand(cbf, **kwargs)
618 if result.returncode:
619 cros_build_lib.Die('cros_bundle_firmware failed')
620
621 if not dest or not result.returncode:
622 cros_build_lib.Info('Image is available at %s/out/image.bin' % outdir)
623 else:
624 if result.returncode:
625 cros_build_lib.Die('Failed to write image to board')
626 else:
627 cros_build_lib.Info('Image written to board with %s' %
628 ' '.join(dest + servo))
629
630
631def main(argv):
632 """Main function for script to build/write firmware.
633
634 Args:
635 argv: Program arguments.
636 """
637 options, args = ParseCmdline(argv)
638 base = SetupBuild(options)
639
640 with parallel.BackgroundTaskRunner(Dumper) as queue:
641 target = args[0] if args else 'all'
642 RunBuild(options, base, target, queue)
643
644 if options.write:
645 WriteFirmware(options)
646
647 if options.objdump:
648 Log('Writing diasssembly files')