blob: 5384b4c8285055b811cd21e713f24025b18198b2 [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
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:
Simon Glassc7d266d2013-07-18 15:15:57 -0600479 result = cros_build_lib.RunCommand([CompilerTool('size')] + files,
480 **kwargs)
Simon Glass02741682013-05-26 07:07:58 -0700481 if result.returncode:
482 sys.exit()
483
484 # Create disassembly files .dis and .Dis (full dump)
485 for f in files:
486 base = os.path.splitext(f)[0]
487 if options.objdump:
488 queue.put(('-d', f, base + '.dis'))
489 queue.put(('-D', f, base + '.Dis'))
490 else:
491 # Remove old files which otherwise might be confusing
492 osutils.SafeUnlink(base + '.dis')
493 osutils.SafeUnlink(base + '.Dis')
494
495 Log('Output directory %s' % outdir)
496
497
498def WriteFirmware(options):
499 """Write firmware to the board.
500
501 This uses cros_bundle_firmware to create a firmware image and write it to
502 the board.
503
504 Args:
505 options: Command line options
506 """
507 flash = []
508 kernel = []
509 run = []
510 secure = []
511 servo = []
512 silent = []
513 verbose_arg = []
514
515 bl2 = ['--bl2', '%s/spl/%s-spl.bin' % (outdir, smdk)]
516
517 if options.use_defaults:
518 bl1 = []
519 bmpblk = []
520 ecro = []
521 ecrw = []
522 defaults = []
523 else:
524 bl1 = ['--bl1', '##/build/%s/firmware/u-boot.bl1.bin' % options.board]
525 bmpblk = ['--bmpblk', '##/build/%s/firmware/bmpblk.bin' % options.board]
526 ecro = ['--ecro', '##/build/%s/firmware/ec.RO.bin' % options.board]
527 ecrw = ['--ec', '##/build/%s/firmware/ec.RW.bin' % options.board]
528 defaults = ['-D']
529
530 if arch == 'x86':
531 seabios = ['--seabios',
532 '##/build/%s/firmware/seabios.cbfs' % options.board]
533 else:
534 seabios = []
535
536 if options.sdcard:
537 dest = 'sd:.'
538 elif arch == 'x86':
539 dest = 'em100'
540 elif arch == 'sandbox':
541 dest = ''
542 else:
543 dest = 'usb'
544
545 port = SERVO_PORT.get(options.board, '')
546 if port:
547 servo = ['--servo', '%d' % port]
548
549 if options.flash:
550 flash = ['-F', 'spi']
551
552 if options.verbose:
553 verbose_arg = ['-v', '%s' % options.verbose]
554
555 if options.secure:
556 secure += ['--bootsecure', '--bootcmd', 'vboot_twostop']
557
558 if not options.verified:
559 # Make a small image, without GBB, etc.
560 secure.append('-s')
561
562 if options.kernel:
563 kernel = ['--kernel', '##/build/%s/boot/vmlinux.uimg' % options.board]
564
565 if not options.console:
566 silent = ['--add-config-int', 'silent-console', '1']
567
568 if not options.run:
569 run = ['--bootcmd', 'none']
570
571 if arch != 'sandbox' and not in_chroot and servo:
572 if dest == 'usb':
573 cros_build_lib.Warning('Image cannot be written to board')
574 dest = ''
575 servo = []
576 elif dest == 'em100':
577 cros_build_lib.Warning('Please reset the board manually to boot firmware')
578 servo = []
579
580 if not servo:
581 cros_build_lib.Warning('(sadly dut-control does not work outside chroot)')
582
583 if dest:
584 dest = ['-w', dest]
585 else:
586 dest = []
587
588 soc = SOCS.get(board)
589 if not soc:
590 soc = SOCS.get(uboard, '')
591 dt_name = DEFAULT_DTS.get(options.board, options.board)
592 dts_file = 'board/%s/dts/%s%s.dts' % (vendor, soc, dt_name)
593 Log('Device tree: %s' % dts_file)
594
595 if arch == 'sandbox':
596 uboot_fname = '%s/u-boot' % outdir
597 else:
598 uboot_fname = '%s/u-boot.bin' % outdir
599
600 cbf = ['%s/platform/dev/host/cros_bundle_firmware' % src_root,
601 '-b', options.board,
602 '-d', dts_file,
603 '-I', 'arch/%s/dts' % arch, '-I', 'cros/dts',
604 '-u', uboot_fname,
605 '-O', '%s/out' % outdir,
606 '-M', family]
607
608 for other in [bl1, bl2, bmpblk, defaults, dest, ecro, ecrw, flash, kernel,
609 run, seabios, secure, servo, silent, verbose_arg]:
610 if other:
611 cbf += other
612 if options.cbfargs:
613 for item in options.cbfargs:
614 cbf += item.split(' ')
615 os.environ['PYTHONPATH'] = ('%s/platform/dev/host/lib:%s/..' %
616 (src_root, src_root))
617 Log(' '.join(cbf))
618 result = cros_build_lib.RunCommand(cbf, **kwargs)
619 if result.returncode:
620 cros_build_lib.Die('cros_bundle_firmware failed')
621
622 if not dest or not result.returncode:
623 cros_build_lib.Info('Image is available at %s/out/image.bin' % outdir)
624 else:
625 if result.returncode:
626 cros_build_lib.Die('Failed to write image to board')
627 else:
628 cros_build_lib.Info('Image written to board with %s' %
629 ' '.join(dest + servo))
630
631
632def main(argv):
633 """Main function for script to build/write firmware.
634
635 Args:
636 argv: Program arguments.
637 """
638 options, args = ParseCmdline(argv)
639 base = SetupBuild(options)
640
641 with parallel.BackgroundTaskRunner(Dumper) as queue:
642 target = args[0] if args else 'all'
643 RunBuild(options, base, target, queue)
644
645 if options.write:
646 WriteFirmware(options)
647
648 if options.objdump:
649 Log('Writing diasssembly files')