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