blob: 413ba14fc5a8545b923b77ef41893ee2477c575e [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,
243 help='Create magic flasher')
244 parser.add_option('-i', '--incremental', action='store_true', default=False,
245 help="Don't reconfigure and clean")
246 parser.add_option('-k', '--kernel', action='store_true', default=False,
247 help='Send kernel to board also')
248 parser.add_option('-O', '--objdump', action='store_true', default=False,
249 help='Write disassembly output')
250 parser.add_option('-r', '--run', action='store_false', default=True,
251 help='Run the boot command')
252 parser.add_option('--ro', action='store_true', default=False,
253 help='Create Chrome OS read-only image')
254 parser.add_option('--rw', action='store_true', default=False,
255 help='Create Chrome OS read-write image')
256 parser.add_option('-s', '--separate', action='store_false', default=True,
257 help='Link device tree into U-Boot, instead of separate')
258 parser.add_option('-S', '--secure', action='store_true', default=False,
259 help='Use vboot_twostop secure boot')
260 parser.add_option('--small', action='store_true', default=False,
261 help='Create Chrome OS small image')
262 parser.add_option('-t', '--trace', action='store_true', default=False,
263 help='Enable trace support')
264 parser.add_option('-v', '--verbose', type='int', default=0,
265 help='Make cros_bundle_firmware verbose')
266 parser.add_option('-V', '--verified', action='store_true', default=False,
267 help='Include Chrome OS verified boot components')
268 parser.add_option('-w', '--write', action='store_false', default=True,
269 help="Don't write image to board using usb/em100")
270 parser.add_option('-x', '--sdcard', action='store_true', default=False,
271 help='Write to SD card instead of USB/em100')
272 parser.add_option('-z', '--size', action='store_true', default=False,
273 help='Display U-Boot image size')
274 return parser.parse_args(argv)
275
276
277def SetupBuild(options):
278 """Set up parameters needed for the build.
279
280 This checks the current environment and options and sets up various things
281 needed for the build, including 'base' which holds the base flags for
282 passing to the U-Boot Makefile.
283
284 Args:
285 options: Command line options
286
287 Returns:
288 Base flags to use for U-Boot, as a list.
289 """
290 global arch, board, compiler, family, outdir, smdk, uboard, vendor, verbose
291
292 if not verbose:
293 verbose = options.verbose != 0
294
295 logger.setLevel(options.verbose)
296
297 Log('Building for %s' % options.board)
298
Simon Glass9d9bf942013-07-10 16:32:42 -0700299 # Separate out board_variant string: "peach_pit" becomes "peach", "pit".
300 # But don't mess up upstream boards which use _ in their name.
Simon Glass02741682013-05-26 07:07:58 -0700301 parts = options.board.split('_')
Simon Glass9d9bf942013-07-10 16:32:42 -0700302 if parts[0] in ['daisy', 'peach']:
303 board = parts[0]
304 else:
305 board = options.board
Simon Glass02741682013-05-26 07:07:58 -0700306
307 # To allow this to be run from 'cros_sdk'
308 if in_chroot:
309 os.chdir(os.path.join(src_root, 'third_party', 'u-boot', 'files'))
310
311 base_board = board
312
313 if options.verified:
314 base_board = 'chromeos_%s' % base_board
315
316 uboard = UBOARDS.get(base_board, base_board)
317 Log('U-Boot board is %s' % uboard)
318
319 # Pull out some information from the U-Boot boards config file
320 family = None
321 with open('boards.cfg') as f:
322 for line in f:
323 if uboard in line:
324 if line[0] == '#':
325 continue
326 fields = line.split()
327 if not fields:
328 continue
329 arch = fields[1]
330 fields += [None, None, None]
331 smdk = fields[3]
332 vendor = fields[4]
333 family = fields[5]
334 break
335 if not arch:
336 cros_build_lib.Die("Selected board '%s' not found in boards.cfg." % board)
337
338 vboot = os.path.join('build', board, 'usr')
339 if arch == 'x86':
340 family = 'em100'
341 if in_chroot:
342 compiler = 'i686-pc-linux-gnu-'
343 else:
344 compiler = '/opt/i686/bin/i686-unknown-elf-'
345 elif arch == 'arm':
346 if in_chroot:
347 # Use the Chrome OS toolchain
348 compiler = 'armv7a-cros-linux-gnueabi-'
349 else:
350 compiler = glob.glob('/opt/linaro/gcc-linaro-arm-linux-*/bin/*gcc')
351 if not compiler:
352 cros_build_lib.Die("""Please install an ARM toolchain for your machine.
353'Install a Linaro toolchain from:'
354'https://launchpad.net/linaro-toolchain-binaries'
355'or see cros/commands/cros_chrome_sdk.py.""")
356 compiler = compiler[0]
357 compiler = re.sub('gcc$', '', compiler)
358 elif arch == 'sandbox':
359 compiler = ''
360 else:
361 cros_build_lib.Die("Selected arch '%s' not supported." % arch)
362
363 if not options.build:
364 options.incremental = True
365
366 cpus = multiprocessing.cpu_count()
367
Simon Glassb89ae892013-07-18 15:23:35 -0600368 outdir = os.path.join(OUT_DIR, uboard)
Simon Glass02741682013-05-26 07:07:58 -0700369 base = [
370 'make',
371 '-j%d' % cpus,
372 'O=%s' % outdir,
373 'ARCH=%s' % arch,
374 'CROSS_COMPILE=%s' % compiler,
375 '--no-print-directory',
376 'HOSTSTRIP=true',
377 'DEV_TREE_SRC=%s-%s' % (family, options.dt),
378 'QEMU_ARCH=']
379
380 if options.verbose < 2:
381 base.append('-s')
382
383 if options.ro and options.rw:
384 cros_build_lib.Die('Cannot specify both --ro and --rw options')
385 if options.ro:
386 base.append('CROS_RO=1')
387 options.small = True
388
389 if options.rw:
390 base.append('CROS_RW=1')
391 options.small = True
392
393 if options.small:
394 base.append('CROS_SMALL=1')
395 else:
396 base.append('CROS_FULL=1')
397
398 if options.verified:
399 base += [
400 'VBOOT=%s' % vboot,
401 'MAKEFLAGS_VBOOT=DEBUG=1',
402 'QUIET=1',
403 'CFLAGS_EXTRA_VBOOT=-DUNROLL_LOOPS',
404 'VBOOT_SOURCE=%s/platform/vboot_reference' % src_root]
Simon Glass9d9bf942013-07-10 16:32:42 -0700405 base.append('VBOOT_DEBUG=1')
Simon Glass02741682013-05-26 07:07:58 -0700406
407 # Handle the Chrome OS USE_STDINT workaround. Vboot needs <stdint.h> due
408 # to a recent change, the need for which I didn't fully understand. But
409 # U-Boot doesn't normally use this. We have added an option to U-Boot to
410 # enable use of <stdint.h> and without it vboot will fail to build. So we
411 # need to enable it where ww can. We can't just enable it always since
412 # that would prevent this script from building other non-Chrome OS boards
413 # with a different (older) toolchain, or Chrome OS boards without vboot.
414 # So use USE_STDINT if the toolchain supports it, and not if not. This
415 # file was originally part of glibc but has recently migrated to the
416 # compiler so it is reasonable to use it with a stand-alone program like
417 # U-Boot. At this point the comment has got long enough that we may as
418 # well include some poetry which seems to be sorely lacking the code base,
419 # so this is from Ogden Nash:
420 # To keep your marriage brimming
421 # With love in the loving cup,
422 # Whenever you're wrong, admit it;
423 # Whenever you're right, shut up.
424 cmd = [CompilerTool('gcc'), '-ffreestanding', '-x', 'c', '-c', '-']
425 result = cros_build_lib.RunCommandCaptureOutput(cmd,
426 input='#include <stdint.h>',
427 **kwargs)
428 if result.returncode == 0:
429 base.append('USE_STDINT=1')
430
431 if options.trace:
432 base.append('FTRACE=1')
433 if options.separate:
434 base.append('DEV_TREE_SEPARATE=1')
435
436 if options.incremental:
437 # Get the correct board for cros_write_firmware
438 config_mk = '%s/include/config.mk' % outdir
439 if not os.path.exists(config_mk):
440 cros_build_lib.Warning('No build found for %s - dropping -i' % board)
441 options.incremental = False
442
443 config_mk = 'include/config.mk'
444 if os.path.exists(config_mk):
445 cros_build_lib.Warning("Warning: '%s' exists, try 'make distclean'"
446 % config_mk)
447
448 # For when U-Boot supports ccache
449 # See http://patchwork.ozlabs.org/patch/245079/
450 if use_ccache:
451 os.environ['CCACHE'] = 'ccache'
452
453 return base
454
455
456def RunBuild(options, base, target, queue):
457 """Run the U-Boot build.
458
459 Args:
460 options: Command line options.
461 base: Base U-Boot flags.
462 target: Target to build.
463 queue: A parallel queue to add jobs to.
464 """
465 Log('U-Boot build flags: %s' % ' '.join(base))
466
467 # Reconfigure U-Boot.
468 if not options.incremental:
469 # Ignore any error from this, some older U-Boots fail on this.
470 cros_build_lib.RunCommand(base + ['distclean'], **kwargs)
471 result = cros_build_lib.RunCommand(base + ['%s_config' % uboard], **kwargs)
472 if result.returncode:
473 sys.exit()
474
475 # Do the actual build.
476 if options.build:
477 result = cros_build_lib.RunCommand(base + [target], **kwargs)
478 if result.returncode:
479 sys.exit()
480
481 files = ['%s/u-boot' % outdir]
482 spl = glob.glob('%s/spl/u-boot-spl' % outdir)
483 if spl:
484 files += spl
485 if options.size:
Simon Glassc7d266d2013-07-18 15:15:57 -0600486 result = cros_build_lib.RunCommand([CompilerTool('size')] + files,
487 **kwargs)
Simon Glass02741682013-05-26 07:07:58 -0700488 if result.returncode:
489 sys.exit()
490
491 # Create disassembly files .dis and .Dis (full dump)
492 for f in files:
493 base = os.path.splitext(f)[0]
494 if options.objdump:
495 queue.put(('-d', f, base + '.dis'))
496 queue.put(('-D', f, base + '.Dis'))
497 else:
498 # Remove old files which otherwise might be confusing
499 osutils.SafeUnlink(base + '.dis')
500 osutils.SafeUnlink(base + '.Dis')
501
502 Log('Output directory %s' % outdir)
503
504
505def WriteFirmware(options):
506 """Write firmware to the board.
507
508 This uses cros_bundle_firmware to create a firmware image and write it to
509 the board.
510
511 Args:
512 options: Command line options
513 """
514 flash = []
515 kernel = []
516 run = []
517 secure = []
518 servo = []
519 silent = []
520 verbose_arg = []
521
522 bl2 = ['--bl2', '%s/spl/%s-spl.bin' % (outdir, smdk)]
523
524 if options.use_defaults:
525 bl1 = []
526 bmpblk = []
527 ecro = []
528 ecrw = []
529 defaults = []
530 else:
531 bl1 = ['--bl1', '##/build/%s/firmware/u-boot.bl1.bin' % options.board]
532 bmpblk = ['--bmpblk', '##/build/%s/firmware/bmpblk.bin' % options.board]
533 ecro = ['--ecro', '##/build/%s/firmware/ec.RO.bin' % options.board]
534 ecrw = ['--ec', '##/build/%s/firmware/ec.RW.bin' % options.board]
535 defaults = ['-D']
536
537 if arch == 'x86':
538 seabios = ['--seabios',
539 '##/build/%s/firmware/seabios.cbfs' % options.board]
540 else:
541 seabios = []
542
543 if options.sdcard:
544 dest = 'sd:.'
545 elif arch == 'x86':
546 dest = 'em100'
547 elif arch == 'sandbox':
548 dest = ''
549 else:
550 dest = 'usb'
551
552 port = SERVO_PORT.get(options.board, '')
553 if port:
554 servo = ['--servo', '%d' % port]
555
556 if options.flash:
557 flash = ['-F', 'spi']
558
559 if options.verbose:
560 verbose_arg = ['-v', '%s' % options.verbose]
561
562 if options.secure:
563 secure += ['--bootsecure', '--bootcmd', 'vboot_twostop']
564
565 if not options.verified:
566 # Make a small image, without GBB, etc.
567 secure.append('-s')
568
569 if options.kernel:
570 kernel = ['--kernel', '##/build/%s/boot/vmlinux.uimg' % options.board]
571
572 if not options.console:
573 silent = ['--add-config-int', 'silent-console', '1']
574
575 if not options.run:
576 run = ['--bootcmd', 'none']
577
578 if arch != 'sandbox' and not in_chroot and servo:
579 if dest == 'usb':
580 cros_build_lib.Warning('Image cannot be written to board')
581 dest = ''
582 servo = []
583 elif dest == 'em100':
584 cros_build_lib.Warning('Please reset the board manually to boot firmware')
585 servo = []
586
587 if not servo:
588 cros_build_lib.Warning('(sadly dut-control does not work outside chroot)')
589
590 if dest:
591 dest = ['-w', dest]
592 else:
593 dest = []
594
595 soc = SOCS.get(board)
596 if not soc:
597 soc = SOCS.get(uboard, '')
598 dt_name = DEFAULT_DTS.get(options.board, options.board)
599 dts_file = 'board/%s/dts/%s%s.dts' % (vendor, soc, dt_name)
600 Log('Device tree: %s' % dts_file)
601
602 if arch == 'sandbox':
603 uboot_fname = '%s/u-boot' % outdir
604 else:
605 uboot_fname = '%s/u-boot.bin' % outdir
606
607 cbf = ['%s/platform/dev/host/cros_bundle_firmware' % src_root,
608 '-b', options.board,
609 '-d', dts_file,
610 '-I', 'arch/%s/dts' % arch, '-I', 'cros/dts',
611 '-u', uboot_fname,
612 '-O', '%s/out' % outdir,
613 '-M', family]
614
615 for other in [bl1, bl2, bmpblk, defaults, dest, ecro, ecrw, flash, kernel,
616 run, seabios, secure, servo, silent, verbose_arg]:
617 if other:
618 cbf += other
619 if options.cbfargs:
620 for item in options.cbfargs:
621 cbf += item.split(' ')
622 os.environ['PYTHONPATH'] = ('%s/platform/dev/host/lib:%s/..' %
623 (src_root, src_root))
624 Log(' '.join(cbf))
625 result = cros_build_lib.RunCommand(cbf, **kwargs)
626 if result.returncode:
627 cros_build_lib.Die('cros_bundle_firmware failed')
628
629 if not dest or not result.returncode:
630 cros_build_lib.Info('Image is available at %s/out/image.bin' % outdir)
631 else:
632 if result.returncode:
633 cros_build_lib.Die('Failed to write image to board')
634 else:
635 cros_build_lib.Info('Image written to board with %s' %
636 ' '.join(dest + servo))
637
638
639def main(argv):
640 """Main function for script to build/write firmware.
641
642 Args:
643 argv: Program arguments.
644 """
645 options, args = ParseCmdline(argv)
646 base = SetupBuild(options)
647
648 with parallel.BackgroundTaskRunner(Dumper) as queue:
649 target = args[0] if args else 'all'
650 RunBuild(options, base, target, queue)
651
652 if options.write:
653 WriteFirmware(options)
654
655 if options.objdump:
656 Log('Writing diasssembly files')