diff --git a/app/controllers/cameras/socs_controller.rb b/app/controllers/cameras/socs_controller.rb index f71afa5..56b3266 100644 --- a/app/controllers/cameras/socs_controller.rb +++ b/app/controllers/cameras/socs_controller.rb @@ -107,26 +107,24 @@ def update # Against FLASH_CHIP rather than for blankness, because a chip this site # does not know is not a choice either. Camera#flash_size_hex and friends # fall through to their 8MB branch for anything unrecognised, while - # @flash_type_command below would go on to render `run setnor64m` and a - # printenv hint naming three variables no bootloader defines. Nothing + # Camera#bootloader_macro_suffix would go on to render `run setnor64m` and + # a printenv hint naming three variables no bootloader defines. Nothing # calls valid? on a Camera, so this is the only thing standing between the # query string and the commands. @camera.flash_type = @camera.soc.default_flash_chip unless @camera.flash_type.in?(Camera::FLASH_CHIP) - # The bootloader macros are named after the layout, not the chip. This - # used to be the flash type with `nor32m` rewritten to `nor16m`, which is - # the same answer for every combination the menu could then produce -- - # there is no mtdpartsnor32m anywhere upstream, so a 32MB part has always - # worn the 16MB layout. Camera#partition_layout says it directly now, and - # says it for the 8MB-layout-on-a-larger-chip case too. - # # After the flash type has settled, not before: the layout defaults to # the chip's own, so reading it first left the page telling a 16MB camera # to `run urnor16m` and then erasing from the 8MB overlay offset, 733,184 # bytes into what it had just written. That is the failure #60 described, # by another route. + # + # What the macros are called is Camera's answer now rather than a separate + # @flash_type_command read here. It was the layout name, which is right + # for the bootloaders that name their macros after the layout and wrong + # for the two that do not name them after anything -- and the view read it + # four times, so the offsets and the commands could disagree. warn_if_layout_changed permitted_params[:partition_layout] - @flash_type_command = @camera.partition_layout if @vendor.name.eql?("SigmaStar") && @camera.flash_type.eql?("nand") render 'cameras/socs/sigmastar_nand_is_weird' diff --git a/app/helpers/installation_helper.rb b/app/helpers/installation_helper.rb index b3ab054..bb004c7 100644 --- a/app/helpers/installation_helper.rb +++ b/app/helpers/installation_helper.rb @@ -147,7 +147,12 @@ def flashing_uboot(c) list_of_commands text end - def flashing_linux(c, c2) + # The suffix comes off the camera rather than being passed in beside it: the + # macros are named for the bootloader's own environment, and on SigmaStar and + # Ingenic that is `uknor`/`urnor` with nothing after it whatever layout is + # being installed. + def flashing_linux(c) + c2 = c.bootloader_macro_suffix text = [] text << do_not_copy_paste unless c.network_interface.eql?('wifi') @@ -179,22 +184,26 @@ def flashing_linux(c, c2) list_of_commands text end - # The three bootloader variables the instructions above actually named, for - # the hint that tells the reader to go and look them up. preparing_environment - # emits `run set…` and flashing_linux emits `run uk…; run ur…`, all from the - # same flash_type_command, so building the hint from it too keeps the three - # in step -- including the nor32m -> nor16m rewrite the controller does. + # The bootloader variables the instructions above actually named, for the hint + # that tells the reader to go and look them up. Camera builds the list from + # the same suffix the commands are built from, so the two stay in step -- + # including the nor32m -> nor16m rewrite and the vendors whose macros carry no + # suffix and have no `set…` to name. # # It used to be a fixed `uknor*, urnor*, setnor*`, which named nothing a NAND # reader had been given and nothing they could find in their own printenv. - def bootloader_variables_html(flash_type_command) - safe_join(%w[uk ur set].map { |prefix| tag.code("#{prefix}#{flash_type_command}") }, ', ') + def bootloader_variables_html(camera) + safe_join(camera.bootloader_variables.map { |name| tag.code(name) }, ', ') end - def preparing_environment(c2) + # Put the bootloader on the layout that was just flashed. One macro where + # there is one, and the `setenv` that macro would have done where there is + # not -- see Camera#layout_commands. Nothing at all when the layout is already + # the bootloader's default, which is why every caller checks first. + def preparing_environment(camera) text = [] text << do_not_copy_paste - text << "run set#{c2}" + text.concat(camera.layout_commands) list_of_commands text end diff --git a/app/models/camera.rb b/app/models/camera.rb index 6b31172..eed29c7 100644 --- a/app/models/camera.rb +++ b/app/models/camera.rb @@ -241,6 +241,63 @@ def partition_layout_name I18n.t("flash_layout.#{partition_layout}") end + # Whether this camera's bootloader carries one NOR mtdparts string and + # unsuffixed macros -- see FlashLayout. NAND is a separate environment with + # its own uknand/urnand/setnand and is not affected either way. + def fixed_mtdparts? + !nand? && FlashLayout.fixed_mtdparts?(soc&.vendor&.name) + end + + # The suffix this camera's bootloader macros actually carry. `uknor8m` and + # friends on HiSilicon and Goke, `uknand` on NAND, and plain `uknor`/`urnor` + # on SigmaStar and Ingenic, whose environment has no suffixed macro at all -- + # the page has been telling those cameras to `run uknor16m` since it first + # had an expert section, and U-Boot has been answering `## Error: "uknor16m" + # not defined` and flashing nothing. + def bootloader_macro_suffix + return 'nand' if nand? + return 'nor' if fixed_mtdparts? + + partition_layout + end + + # Whether the bootloader already boots this layout without being told. Every + # one of them defaults to the 8MB partitions, and a full-image flash leaves + # the env erased, so that default is what a freshly flashed camera comes up + # with. + # + # NAND is not one of them: mtdpartsubi is not a default anything falls back + # to, and a NAND camera has always been told to `run setnand` after a full + # image like it is now. + def default_bootloader_layout? + !nand? && layout_size <= 8 + end + + # What to run to put the bootloader on this layout, if anything. + # + # HiSilicon and Goke have a macro for it. SigmaStar and Ingenic do not: their + # mtdparts is one string with ${rootmtd} in it, saved unexpanded and expanded + # at boot by `cmdnor`, so the layout is changed by setting that variable and + # the erase length that goes with it. Empty when there is nothing to change, + # which is what rootmtd=5120k already is. + def layout_commands + return ["run set#{bootloader_macro_suffix}"] unless fixed_mtdparts? + return [] if default_bootloader_layout? + + ["setenv rootmtd #{rootfs_max_size.to_i(16) / 1024}k; setenv rootsize #{rootfs_max_size}", + 'saveenv', 'reset'] + end + + # The bootloader variables the instructions above actually named, for the hint + # that tells the reader to go and look them up. Built from the same suffix the + # commands are, so the two cannot drift -- and without a `set…` entry where no + # such variable exists, since the reader would not find it in their printenv. + def bootloader_variables + names = %w[uk ur].map { |prefix| "#{prefix}#{bootloader_macro_suffix}" } + names << "set#{bootloader_macro_suffix}" unless fixed_mtdparts? + names + end + # The NOR numbers come from FlashLayout, which reads them off the bootloader # environment. They used to be spelled out here keyed on firmware_version, # which agreed with the bootloader only for 8MB+Lite and 16MB+Ultimate; see @@ -249,8 +306,12 @@ def partition_layout_name # Keyed on the layout, not on the chip. The two agree for every combination # the menu offered before it grew a second field, and the whole point of the # second field is the ones where they do not. + # + # The vendor goes with it because two of them have a bootloader whose rootfs + # offset does not move between layouts. Without it a 16MB SigmaStar or Ingenic + # camera is handed 0x350000, which its bootloader never reads. def nor_layout - FlashLayout.nor(layout_size) + FlashLayout.nor(layout_size, soc&.vendor&.name) end def kernel_max_size diff --git a/app/models/firmware.rb b/app/models/firmware.rb index a0e586a..b646865 100644 --- a/app/models/firmware.rb +++ b/app/models/firmware.rb @@ -448,9 +448,12 @@ def rootfs_member # Same table the installation page renders from, so the image and the # instructions cannot describe different partition layouts. Keyed on the # layout rather than the size, which are the same thing for every image built - # before the wizard could tell them apart. + # before the wizard could tell them apart, and on the vendor, because + # SigmaStar and Ingenic have one mtdparts string whose rootfs offset does not + # move -- an image that put it where the other table says is one their + # bootloader cannot boot. def nor_layout - @nor_layout ||= FlashLayout.nor(@layout) + @nor_layout ||= FlashLayout.nor(@layout, @soc.vendor.name) end def kernel_offset diff --git a/app/models/flash_layout.rb b/app/models/flash_layout.rb index 2e4ce6a..0426f6b 100644 --- a/app/models/flash_layout.rb +++ b/app/models/flash_layout.rb @@ -33,30 +33,69 @@ class FlashLayout overlay_offset: 0xD50000 }.freeze }.freeze - # SigmaStar and Ingenic used to be pinned to the 8MB offsets whatever chip was - # chosen, on the grounds that their bootloaders defined no uknor/urnor macros - # to check against. They do. The repositories checked were u-boot-sigmastar - # and u-boot-ingenic, which are not what those SoCs ship; the real ones are - # per-SoC, and u-boot-t20, u-boot-t40 and u-boot-msc313e all carry the same - # pair as the Hisilicon and Goke bootloaders above, down to the byte: + # SigmaStar and Ingenic do not have that pair, and the bootloaders that do are + # not the ones those SoCs ship. # - # mtdpartsnor16m = 256k(boot),64k(env),3072k(kernel),10240k(rootfs),-(rootfs_data) - # uknor16m : sf erase 0x50000 0x300000 urnor16m : sf erase 0x350000 0xa00000 + # OpenIPC/firmware's .github/workflows/uboot.yml is what builds the binaries + # this site links. It clones openipc/u-boot-sigmastar and openipc/u-boot- + # ingenic and runs their build.sh; the SigmaStar one's spinor loop is + # `ssc377 ssc377d ssc377de ssc377qe ssc378de ssc378qe` against + # include/configs/infinity6c.h, which includes configs/sstar-common.h. That + # header is the entire environment every SSC3xx NOR camera boots with: # - # Only the mtd device name differs -- jz_sfc, NOR_FLASH, sfc. + # kernaddr=0x50000 kernsize=0x200000 + # rootaddr=0x250000 rootsize=0x500000 rootmtd=5120k + # uknor / urnor / ubnor <- no size suffix, and no setnor* at all + # CONFIG_BOOTARGS "... mtdparts=NOR_FLASH:256k(boot),64k(env),2048k(kernel), + # ${rootmtd}(rootfs),-(rootfs_data) ..." # - # The pin also could not survive Ultimate on 16MB. Ultimate's NOR rootfs is - # 7820KB on ssc338q and 6752KB on t31, and the 8MB layout gives rootfs 5120KB. - # There is no arrangement in which those two vendors offer Ultimate on a 16MB - # chip and keep the 8MB geometry. + # A repo-wide grep for uknor8m|uknor16m|urnor8m|urnor16m|setnor8m|setnor16m| + # mtdpartsnor returns nothing in either repo; u-boot-ingenic's + # include/configs/isvp_common.h carries the same unsuffixed uknor/urnor. + # u-boot-msc313e, u-boot-t20 and u-boot-t40 do define the suffixed pair, which + # is what the note this replaces was reading -- but no released binary is + # built from them, so no camera runs them. # - # This is the one place the chip size decides, for every vendor. What has to - # travel with it is the instruction to run `setnor16m`: every one of these - # bootloaders defaults mtdparts to the 8MB layout, and flashing a full image - # leaves the env erased, so a 16MB camera that is never told to switch boots - # with 8MB partitions. update.html.erb used to suppress that instruction for - # these same two vendors and no longer does. - def self.nor(flash_size_mb) - flash_size_mb.to_i <= 8 ? NOR[8] : NOR[16] + # So these two have one mtdparts string, the kernel partition is 2048k inside + # it, and the only thing that varies is ${rootmtd}. The rootfs starts at + # 0x250000 at every chip size; what a larger chip buys is a longer rootfs, not + # one further up. + # + # Handing them NOR[16] put the rootfs at 0x350000, which is where the images + # openipc.org serves today for ssc377qe have it: layout=16 carries "hsqs" at + # 0x350000 and 0xff at 0x250000, and its env region at 0x40000 is blank, so + # the camera comes up on the compiled-in bootargs, looks for the rootfs at + # 0x250000 and panics on root mount. layout=8 has it at 0x250000 and boots. + FIXED_MTDPARTS_VENDORS = %w[SigmaStar Ingenic].freeze + + # The same two questions the table above answers, for a bootloader whose + # rootfs cannot move. 16 is not a different partition map, it is `rootmtd` + # and the erase length that goes with it set to 10240k -- which is also the + # only way an Ultimate rootfs fits: 7832KB on ssc338q, 7252KB on ssc30kq and + # 6772KB on t31, against the 5120KB the default leaves. + FIXED_MTDPARTS_NOR = { + 8 => { kernel_offset: 0x50000, kernel_max_size: 0x200000, + rootfs_offset: 0x250000, rootfs_max_size: 0x500000, + overlay_offset: 0x750000 }.freeze, + 16 => { kernel_offset: 0x50000, kernel_max_size: 0x200000, + rootfs_offset: 0x250000, rootfs_max_size: 0xA00000, + overlay_offset: 0xC50000 }.freeze + }.freeze + + # Whether this vendor's NOR bootloader has one mtdparts string rather than a + # mtdpartsnor8m/mtdpartsnor16m pair to switch between. + def self.fixed_mtdparts?(vendor_name) + FIXED_MTDPARTS_VENDORS.include?(vendor_name.to_s) + end + + # The chip size decides which entry, and the vendor decides which table. What + # has to travel with the 16MB entry either way is the instruction to put the + # bootloader on it: all of these default to the 8MB partitions -- mtdparts on + # HiSilicon and Goke, rootmtd=5120k on SigmaStar and Ingenic -- and flashing a + # full image leaves the env erased, so that default is what boots. See + # Camera#layout_commands for what each family is told to run. + def self.nor(flash_size_mb, vendor_name = nil) + table = fixed_mtdparts?(vendor_name) ? FIXED_MTDPARTS_NOR : NOR + table[flash_size_mb.to_i <= 8 ? 8 : 16] end end diff --git a/app/views/cameras/socs/update.html.erb b/app/views/cameras/socs/update.html.erb index a8b1ad1..8b7a178 100644 --- a/app/views/cameras/socs/update.html.erb +++ b/app/views/cameras/socs/update.html.erb @@ -76,17 +76,15 @@ <% end %> <%= flashing_everything(@camera) %>
<%= t('firmware.installation.flashing_full.continue') %>
- <%# SigmaStar and Ingenic were exempted here in 64d37db and 00332d1 and - are not any more. Their bootloaders default mtdparts to the 8MB - layout like every other one, a full-image flash leaves the env - erased so that default is what the camera boots with, and the - expert section below has always told them to run setnor16m - regardless -- so the exemption made this page contradict itself. - It also cannot coexist with Ultimate on 16MB: that rootfs is - 7820KB on ssc338q against the 5120KB the 8MB layout allows. %> - <% unless @flash_type_command.eql?('nor8m') %> + <%# Every one of these bootloaders defaults mtdparts to the 8MB layout + and a full-image flash leaves the env erased, so that default is + what the camera boots with and anything else has to be set here. + Asked of the camera rather than of the layout name: SigmaStar and + Ingenic have no set* macro to run, and what they are given instead + is the setenv that macro would have done. %> + <% unless @camera.default_bootloader_layout? %><%= t('firmware.installation.flashing_full.continue2') %>
- <%= preparing_environment(@flash_type_command) %> + <%= preparing_environment(@camera) %> <% end %> @@ -126,15 +124,21 @@ -<%= t('firmware.installation.flashing_footfs.info') %>
- <%= preparing_environment(@flash_type_command) %> -<%= t('firmware.installation.flashing_footfs.continue') %>
+ <%# Skipped where there is nothing to run. This step used to render + unconditionally, so a SigmaStar or Ingenic reader was given `run + setnor8m` -- a variable their bootloader does not define -- under a + heading telling them it was required. %> + <% if @camera.layout_commands.any? %> +<%= t('firmware.installation.flashing_footfs.info') %>
+ <%= preparing_environment(@camera) %> +<%= t('firmware.installation.flashing_footfs.continue') %>
+<%= t('.sdcard_required_3') %>
<%= t('firmware.info_html', commands: bootloader_variables_html(@flash_type_command)) %>
+<%= t('firmware.info_html', commands: bootloader_variables_html(@camera)) %>
<%= t('.advanced_instruction_link') %> diff --git a/test/controllers/socs_controller_test.rb b/test/controllers/socs_controller_test.rb index d93bb1c..18be934 100644 --- a/test/controllers/socs_controller_test.rb +++ b/test/controllers/socs_controller_test.rb @@ -609,12 +609,11 @@ def submit(soc, flash_type, firmware_version: 'lite', partition_layout: nil, loc # --- SigmaStar and Ingenic on 16MB --- - # FlashLayout pinned these two vendors to the 8MB offsets whatever chip was - # picked, so the page told a 16MB camera to `run uknor16m; run urnor16m` -- - # writing the rootfs to 0x350000..0xd50000 using the bootloader's own macros - # -- and then erased from 0x750000, 733,184 bytes inside it. u-boot-msc313e, - # u-boot-t20 and u-boot-t40 all define mtdpartsnor16m identically to the - # Hisilicon and Goke ones, so there was never a reason to treat them apart. + # The bootloader these two actually ship -- openipc/u-boot-sigmastar and + # openipc/u-boot-ingenic, which is what OpenIPC/firmware's uboot.yml builds + # the released binaries from -- has one NOR mtdparts string with a fixed + # 2048k kernel and ${rootmtd} after it. The rootfs is at 0x250000 at every + # chip size, and there is no uknor16m, urnor16m or setnor16m to run. def soc_of(vendor_name) vendor = Vendor.create!(name: vendor_name) Soc.create!(vendor:, model: 'TS338Q', status: 'done', load_address: '0x82000000', @@ -622,54 +621,88 @@ def soc_of(vendor_name) linux_filename: 'openipc.ts338q-nor-lite.tgz') end - # FlashLayout pinned these two vendors to the 8MB offsets whatever chip was - # picked, so the page told a 16MB camera to `run uknor16m; run urnor16m` -- - # writing the rootfs to 0x350000..0xd50000 using the bootloader's own macros - # -- and then erased from 0x750000, 733,184 bytes inside it. - def assert_sixteen_meg_offsets(vendor_name) + # A 16MB layout on these two means rootmtd=10240k, not a rootfs 1MB further + # up. Handing them the other table put it at 0x350000, where their bootloader + # never looks: the full image openipc.org serves for ssc377qe today carries + # "hsqs" at 0x350000, 0xff at 0x250000 and a blank env at 0x40000, so the + # camera boots on the compiled-in bootargs and panics on root mount. + def assert_rootfs_stays_at_250000(vendor_name) soc = soc_of(vendor_name) with_release_index(*every_edition_for(soc)) do submit(soc, 'nor16m', firmware_version: 'ultimate') - assert_match 'sf erase 0xD50000', response.body + assert_match 'sf erase 0xC50000', response.body + assert_no_match(/sf erase 0xD50000/, response.body) assert_no_match(/sf erase 0x750000/, response.body) end end - # Those offsets are only right if the camera is running the 16MB mtdparts, and - # every one of these bootloaders defaults to the 8MB one. A full-image flash - # leaves the env erased, so that default is what boots. These two vendors used - # to be the only ones not told to run setnor16m afterwards. - def assert_told_to_remap_partitions(vendor_name) + # Whatever is run has to exist. `run uknor16m` returns `## Error: "uknor16m" + # not defined` on these cameras and flashes nothing, silently. + def assert_unsuffixed_macros(vendor_name) soc = soc_of(vendor_name) with_release_index(*every_edition_for(soc)) do - submit(soc, 'nor16m') + submit(soc, 'nor16m', firmware_version: 'ultimate') + + assert_match 'run uknor; run urnor', response.body + assert_no_match(/uknor16m|urnor16m|uknor8m|urnor8m/, response.body) + assert_no_match(/setnor/, response.body) + end + end + + # The 16MB layout still has to be switched on -- rootmtd defaults to 5120k and + # a full-image flash leaves the env erased -- but with the setenv their + # bootloader understands rather than a macro it does not define. + def assert_told_to_set_rootmtd(vendor_name) + soc = soc_of(vendor_name) + + with_release_index(*every_edition_for(soc)) do + submit(soc, 'nor16m', firmware_version: 'ultimate') - # The expert section further down emits `run setnor16m` for everybody and - # always has, so matching the string alone proves nothing. What was - # suppressed is the copy of it in the full-image section, which arrives - # with the flashing_full.continue2 sentence in front of it. assert_match 'remap ROM partitioning according to your flash size', response.body - assert_equal 2, response.body.scan('run setnor16m').size + assert_match 'setenv rootmtd 10240k; setenv rootsize 0xA00000', response.body end end - test 'a 16MB SigmaStar submission gets the 16MB layout like everyone else' do - assert_sixteen_meg_offsets('SigmaStar') + test 'a 16MB SigmaStar submission keeps the rootfs at 0x250000' do + assert_rootfs_stays_at_250000('SigmaStar') end - test 'a 16MB Ingenic submission gets the 16MB layout like everyone else' do - assert_sixteen_meg_offsets('Ingenic') + test 'a 16MB Ingenic submission keeps the rootfs at 0x250000' do + assert_rootfs_stays_at_250000('Ingenic') end - test 'a 16MB SigmaStar camera is told to run setnor16m after a full flash' do - assert_told_to_remap_partitions('SigmaStar') + test 'a SigmaStar camera is given the macros its bootloader defines' do + assert_unsuffixed_macros('SigmaStar') end - test 'a 16MB Ingenic camera is told to run setnor16m after a full flash' do - assert_told_to_remap_partitions('Ingenic') + test 'an Ingenic camera is given the macros its bootloader defines' do + assert_unsuffixed_macros('Ingenic') + end + + test 'a 16MB SigmaStar camera is told to set rootmtd after a full flash' do + assert_told_to_set_rootmtd('SigmaStar') + end + + test 'a 16MB Ingenic camera is told to set rootmtd after a full flash' do + assert_told_to_set_rootmtd('Ingenic') + end + + # The 8MB layout is what rootmtd=5120k already is, so there is nothing to run + # and nothing to tell the reader. This step used to render regardless, under a + # heading saying it was required, naming a variable that does not exist. + test 'a SigmaStar camera on the default layout is told to run nothing' do + soc = soc_of('SigmaStar') + + with_release_index(*every_edition_for(soc)) do + submit(soc, 'nor16m', partition_layout: 'nor8m') + + assert_match 'sf erase 0x750000', response.body + assert_no_match(/setenv rootmtd/, response.body) + assert_no_match(/run set/, response.body) + end end # --- the pages that send the visitor to the wiki instead --- diff --git a/test/models/camera_test.rb b/test/models/camera_test.rb index eae0aa6..3a6f56f 100644 --- a/test/models/camera_test.rb +++ b/test/models/camera_test.rb @@ -196,6 +196,96 @@ def camera(flash_type:, firmware_version: 'ultimate') end end + # --- bootloaders with one mtdparts string --- + + # openipc/u-boot-sigmastar and openipc/u-boot-ingenic -- the repositories + # OpenIPC/firmware's uboot.yml builds the released binaries from -- carry one + # NOR mtdparts with a fixed 2048k kernel and ${rootmtd} behind it, plain + # uknor/urnor, and no setnor* of any kind. + StubVendor = Struct.new(:name) + StubSoc = Struct.new(:vendor) + + def camera_of(vendor_name, flash_type:, firmware_version: 'lite', layout: nil) + c = camera(flash_type:, firmware_version:) + c.soc = StubSoc.new(StubVendor.new(vendor_name)) + c.partition_layout = layout if layout + c + end + + test 'a 16MB SigmaStar chip keeps the rootfs where its bootloader looks' do + c = camera_of('SigmaStar', flash_type: 'nor16m', firmware_version: 'ultimate') + + assert_equal 16, c.layout_size + assert_equal '0x250000', c.rootfs_offset, 'the rootfs does not move on this bootloader' + assert_equal '0xA00000', c.rootfs_max_size, 'what a 16MB chip buys is rootmtd=10240k' + assert_equal '0x200000', c.kernel_max_size, 'the kernel partition is 2048k in the bootargs' + assert_equal '0xC50000', c.overlay_offset + assert_equal '0x3b0000', c.overlay_max_size + end + + test 'Ingenic gets the same treatment as SigmaStar' do + assert_equal '0x250000', camera_of('Ingenic', flash_type: 'nor16m').rootfs_offset + end + + test 'a 16MB chip from any other vendor still moves the rootfs to 0x350000' do + assert_equal '0x350000', camera_of('HiSilicon', flash_type: 'nor16m').rootfs_offset + assert_equal '0x300000', camera_of('HiSilicon', flash_type: 'nor16m').kernel_max_size + end + + test 'SigmaStar macros carry no size suffix, and there is no set macro to name' do + c = camera_of('SigmaStar', flash_type: 'nor16m') + + assert_equal 'nor', c.bootloader_macro_suffix + assert_equal %w[uknor urnor], c.bootloader_variables + end + + test 'other vendors keep the suffixed macros they define' do + c = camera_of('HiSilicon', flash_type: 'nor16m') + + assert_equal 'nor16m', c.bootloader_macro_suffix + assert_equal %w[uknor16m urnor16m setnor16m], c.bootloader_variables + end + + # rootmtd=5120k is the 8MB layout, so a camera already on it has nothing to + # run. The page used to render `run setnor8m` here, which these bootloaders + # answer with `## Error: "setnor8m" not defined`. + test 'the default layout asks a SigmaStar camera to run nothing' do + c = camera_of('SigmaStar', flash_type: 'nor16m', layout: 'nor8m') + + assert_equal 8, c.layout_size + assert_empty c.layout_commands + assert_equal '0x750000', c.overlay_offset + end + + test 'the 16MB layout is set with the variable the bootloader has' do + c = camera_of('SigmaStar', flash_type: 'nor16m', firmware_version: 'ultimate') + + assert_equal ['setenv rootmtd 10240k; setenv rootsize 0xA00000', 'saveenv', 'reset'], + c.layout_commands + end + + test 'a macro-defining vendor is still told to run one' do + assert_equal ['run setnor16m'], camera_of('HiSilicon', flash_type: 'nor16m').layout_commands + assert_equal ['run setnor8m'], camera_of('HiSilicon', flash_type: 'nor8m').layout_commands + end + + # NAND is a separate environment -- uknand, urnand, setnand and mtdpartsubi -- + # and none of this touches it. + test 'nand keeps its own macros whatever the vendor' do + c = camera_of('SigmaStar', flash_type: 'nand', firmware_version: 'ultimate') + + assert_not c.fixed_mtdparts? + assert_equal 'nand', c.bootloader_macro_suffix + assert_equal ['run setnand'], c.layout_commands + end + + # Every existing test builds a Camera with no SoC at all, and the offsets they + # pin have to keep coming out of the table they always did. + test 'a camera with no SoC falls through to the layout table it always used' do + assert_equal '0x350000', camera(flash_type: 'nor16m').rootfs_offset + assert_equal 'nor16m', camera(flash_type: 'nor16m').bootloader_macro_suffix + end + # --- editions that are not published --- test 'an edition upstream does not build is replaced, and says what was asked for' do diff --git a/test/models/firmware_test.rb b/test/models/firmware_test.rb index c406988..1e97b61 100644 --- a/test/models/firmware_test.rb +++ b/test/models/firmware_test.rb @@ -149,6 +149,34 @@ def leftover_temp_files(firmware) assert_equal ("\xFF".b * 0x100), image[16.megabytes - 0x100, 0x100] end + # The image openipc.org serves for ssc377qe today has "hsqs" at 0x350000 and + # 0xff at 0x250000, and its env region at 0x40000 is blank -- so the camera + # comes up on the bootloader's compiled-in bootargs, which say the rootfs + # starts at 0x250000, finds erased flash there and panics on root mount. + # openipc/u-boot-sigmastar has one mtdparts string and the rootfs offset in it + # does not move; 16MB means rootmtd=10240k, not a rootfs 1MB further up. + test 'a 16MB SigmaStar image keeps the rootfs at the only offset its bootloader reads' do + fw = build(model: 'ssc338q', vendor: 'SigmaStar', flash_type: 'nor', size: 16, + members: { 'uImage.ssc338q' => KERNEL, 'rootfs.squashfs.ssc338q' => SQUASHFS }) + fw.generate + image = IO.binread(fw.filepath) + + assert_equal 16.megabytes, image.bytesize + assert_equal KERNEL, image[0x50000, KERNEL.bytesize] + assert_equal SQUASHFS, image[0x250000, SQUASHFS.bytesize] + assert_equal ("\xFF".b * 0x100), image[0x350000, 0x100], 'nothing belongs at the HiSilicon offset' + # rootfs_data begins after a 10240KB rootfs, not after a 5120KB one. + assert_equal ("\xFF".b * 0x100), image[0xC50000, 0x100] + end + + test 'a 16MB Ingenic image does the same' do + fw = build(model: 't31', vendor: 'Ingenic', flash_type: 'nor', size: 16, + members: { 'uImage.t31' => KERNEL, 'rootfs.squashfs.t31' => SQUASHFS }) + fw.generate + + assert_equal SQUASHFS, IO.binread(fw.filepath)[0x250000, SQUASHFS.bytesize] + end + # A request parameter, like the size beside it, so it is refused before it # can decide where anything is written. test 'a layout larger than the chip is refused' do @@ -557,22 +585,31 @@ def leftover_temp_files(firmware) end end - # These two used to be pinned to the 8MB offsets whatever chip was chosen, so - # this asserted 0x250000. u-boot-msc313e, u-boot-t20 and u-boot-t40 all define - # mtdpartsnor16m exactly as the Hisilicon and Goke bootloaders do, and their - # Ultimate rootfs -- 7820KB on ssc338q -- does not fit the 5120KB the 8MB - # layout allows, so the pin could not survive Ultimate on 16MB either. What - # this test is for is unchanged: the image and the page must not describe - # different layouts, whichever offsets are right. - test 'no vendor gets offsets of its own: image and page agree on 16MB' do - %w[SigmaStar Ingenic HiSilicon].each do |vendor| - fw = build(model: 'ssc338q', vendor: vendor, flash_type: 'nor', size: 16, release: 'lite', - members: { 'uImage.ssc338q' => KERNEL, 'rootfs.squashfs.ssc338q' => SQUASHFS }) + # What this test is for is unchanged -- the image and the page must not + # describe different layouts -- but what the right offset is depends on the + # bootloader, so it is asserted per vendor rather than once for everybody. + # 0x350000 where mtdparts switches wholesale between mtdpartsnor8m and + # mtdpartsnor16m; 0x250000 where there is one mtdparts string with a fixed + # 2048k kernel and only ${rootmtd} behind it, which is what + # openipc/u-boot-sigmastar and openipc/u-boot-ingenic ship. + # + # A model of its own per vendor, not one model wearing three: the cache + # filename carries the model and not the vendor, so building the same model + # under three vendors in one test has the second and third read back the + # first's image. Nothing in production can hit that -- a model belongs to one + # vendor -- but a test that did would pass on a stale file. + test 'the image and the page agree on 16MB, on whichever offsets the vendor is on' do + { 'SigmaStar' => ['ssc338q', 0x250000], + 'Ingenic' => ['t31x', 0x250000], + 'HiSilicon' => ['hi3516ev300', 0x350000] }.each do |vendor, (model, offset)| + fw = build(model: model, vendor: vendor, flash_type: 'nor', size: 16, release: 'lite', + members: { "uImage.#{model}" => KERNEL, "rootfs.squashfs.#{model}" => SQUASHFS }) fw.generate camera = Camera.new(flash_type: 'nor16m', firmware_version: 'lite', soc: fw_soc(vendor)) - assert_equal '0x350000', camera.rootfs_offset, "#{vendor} is not on the 16MB rootfs offset" - assert_equal SQUASHFS, IO.binread(fw.filepath)[0x350000, SQUASHFS.bytesize], + assert_equal format('0x%X', offset), camera.rootfs_offset, + "#{vendor} is not on the offset its bootloader reads" + assert_equal SQUASHFS, IO.binread(fw.filepath)[offset, SQUASHFS.bytesize], "#{vendor}: the image does not match the page" end end